#include "mem/sram.h"
#include "mem/dram.h"
#include "device/conv_path.h"
#include "device/conv_dma.h"
#include "device/vector_path.h"
#include "device/vector_path_v2.h"
#include "type.h"
#include "runtime/ops_api.h"
#include "soc/jh7110.h"
#include <iostream>

#define ALIGN_48(x) (((x) + 48 - 1) / 48 * 48)
#define ALIGN_32(x) (((x) + 32 - 1) / 32 * 32)
#define ALIGN_16(x) (((x) + 16 - 1) / 16 * 16)

#define LINE_BUFFER_LEN (32)
static void ifm_param_gen(int ifm_width,int ofm_width,int knl_w,int knl_stride_w,int padding_left,int padding_right,uint32_t &ifm_sw_p0,uint32_t &ifm_sw_p1,uint32_t &ifm_ls_p0,uint32_t &ifm_ls_p1,uint32_t &ofm_sw_p0,uint32_t &ofm_sw_p1,uint32_t &ofm_sw_p2,uint32_t &ofm_sub_width_p0_flag) {
    //std::cout << "*************************THIS IS TC REGION****************************" << std::endl;

    int ofm_w_no_pad           = (ifm_width - knl_w) / knl_stride_w + 1;
    int ofm_w_batch_no_pad     = (LINE_BUFFER_LEN - knl_w) / knl_stride_w + 1;
    int ofm_w_batch_with_pad_l = (LINE_BUFFER_LEN + padding_left - knl_w) / knl_stride_w + 1;

///    uint32_t ifm_sw_p0, ifm_sw_p1;
///    uint32_t ofm_sw_p0, ofm_sw_p1, ofm_sw_p2;
///    uint32_t ifm_ls_p0 = -1 * padding_left + ofm_w_batch_with_pad_l * knl_stride_w;
    ifm_ls_p0 = -1 * padding_left + ofm_w_batch_with_pad_l * knl_stride_w;
///    uint32_t ifm_ls_p1 = ofm_w_batch_no_pad * knl_stride_w;
    ifm_ls_p1 = ofm_w_batch_no_pad * knl_stride_w;

    int ofm_w_quo = ofm_w_no_pad / ofm_w_batch_no_pad;
    int ifm_w_reminder = LINE_BUFFER_LEN * ofm_w_quo - (LINE_BUFFER_LEN - ifm_ls_p1) * (ofm_w_quo - 1) - ifm_width;
    int ofm_w_no_pad_wtt_ifm_sw_l = ifm_width > ifm_ls_p0 + knl_w ? (ifm_width - ifm_ls_p0 - knl_w) / knl_stride_w + 1 : 0;
    int ofm_w_quo_wtt_ifm_sw_l = ofm_w_no_pad_wtt_ifm_sw_l / ofm_w_batch_no_pad;

    uint32_t ofm_w_batch_p0, ofm_w_batch_p1, ofm_w_batch_p2;
///    uint32_t ofm_sub_width_p0_flag = 0;
    ofm_sub_width_p0_flag = 0;

    if (ifm_width > LINE_BUFFER_LEN) {
        if (ifm_w_reminder == 0) {
            //std::cout<<"---------------------ifm_w_reminder == 0-----------------------" << std::endl;

            if (padding_left == 0) {
                //std::cout << "--------------------left padding == 0-------------------------" << std::endl;
                ofm_w_batch_p0 = ofm_w_batch_no_pad;
                ifm_sw_p1 = 0;

                if (padding_right == 0) {
                    ofm_sub_width_p0_flag = 0;
                    ofm_w_batch_p1 = 0;
                    ofm_w_batch_p2 = 0;
                }
                else {
                    ofm_sub_width_p0_flag = 1;
                    ofm_w_batch_p1 = (LINE_BUFFER_LEN + padding_right - knl_w) / knl_stride_w + 1;
                    ofm_w_batch_p2 = 0;
                }
            }
            else {
                //std::cout << "--------------------left padding != 0-------------------------" << std::endl;
                ofm_w_batch_p0 = ofm_w_batch_with_pad_l;
                ofm_w_batch_p1 = ofm_w_batch_no_pad;
                ofm_sub_width_p0_flag = 0;

                if (ofm_w_quo_wtt_ifm_sw_l == 0) {
                    ifm_sw_p1 = ifm_width - ifm_ls_p0;
                    //std::cout << "---------WP 0---------" << std::endl;
                }
                else {
                    if (ifm_width - ifm_ls_p0 >= LINE_BUFFER_LEN * ofm_w_quo_wtt_ifm_sw_l - (LINE_BUFFER_LEN - ifm_ls_p1) * (ofm_w_quo_wtt_ifm_sw_l - 1)) {
                            ifm_sw_p1 = ifm_width - ifm_ls_p0 - ifm_ls_p1 * ofm_w_quo_wtt_ifm_sw_l;
                            //std::cout << "---------WP 1---------" << std::endl;
                    }
                    else {
                            ifm_sw_p1 = ifm_width - ifm_ls_p0 - ifm_ls_p1 * (ofm_w_quo_wtt_ifm_sw_l - 1);
                            //std::cout << "---------WP 2---------" << std::endl;
                    }
                }

                if (padding_right == 0) {
                    if (ifm_sw_p1 >= knl_w) {
                        ofm_w_batch_p2 = (ifm_sw_p1 - knl_w) / knl_stride_w + 1;
                    }
                    else {
                        ofm_w_batch_p2 = 0;
                        ifm_sw_p1 = 0;
                    }
                }
                else {
                    if (ifm_sw_p1 + padding_right >= knl_w) {
                        ofm_w_batch_p2 = (ifm_sw_p1 + padding_right - knl_w) / knl_stride_w + 1;
                    }
                    else {
                        ofm_w_batch_p2 = 0;
                        ifm_sw_p1 = 0;
                    }
                }
            }
        }
        else {
            //std::cout << "------------------------------ifm_w_reminder != 0--------------------------------" << std::endl;

            if (padding_left == 0) {
                //std::cout << "-------------------------left padding == 0-------------------------------" << std::endl;

                if (ifm_width >= LINE_BUFFER_LEN * ofm_w_quo - (LINE_BUFFER_LEN - ifm_ls_p1) * (ofm_w_quo - 1)) {
                    ifm_sw_p1 = ifm_width - ifm_ls_p1 * ofm_w_quo;
                    //std::cout << "-----------WP 0----------" << std::endl;
                }
                else {
                        ifm_sw_p1 = ifm_width - ifm_ls_p1 * (ofm_w_quo - 1);
                        //std::cout << "-----------WP 1----------" << std::endl;
                }

                ofm_w_batch_p0 = ofm_w_batch_no_pad;
                ofm_w_batch_p2 = 0;
                ofm_sub_width_p0_flag = 1;

                if (padding_right == 0) {
                    if (ifm_sw_p1 >= knl_w) {
                        ofm_w_batch_p1 = (ifm_sw_p1 - knl_w) / knl_stride_w + 1;
                    }
                    else {
                        ofm_w_batch_p1 = 0;
                        ifm_sw_p1 = 0;
                    }
                }
               else {
                   if (ifm_sw_p1 + padding_right >= knl_w) {
                       ofm_w_batch_p1 = (ifm_sw_p1 + padding_right - knl_w) / knl_stride_w + 1;
                   }
                   else {
                       ofm_w_batch_p1 = 0;
                       ifm_sw_p1 = 0;
                   }
               }
            }
            else {
                //std::cout << "---------------------left padding != 0------------------------" << std::endl;

                if (ofm_w_quo_wtt_ifm_sw_l == 0) {
                    ifm_sw_p1 = ifm_width - ifm_ls_p0;
                    //std::cout << "--------WP 0-------" << std::endl;
                }
                else {
                    if (ifm_width - ifm_ls_p0 >= LINE_BUFFER_LEN * ofm_w_quo_wtt_ifm_sw_l - (LINE_BUFFER_LEN - ifm_ls_p1) * (ofm_w_quo_wtt_ifm_sw_l - 1)) {
                        ifm_sw_p1 = ifm_width - ifm_ls_p0 - ifm_ls_p1 * ofm_w_quo_wtt_ifm_sw_l;
                        //std::cout << "--------WP 1-------" << std::endl;
                    }
                    else {
                        ifm_sw_p1 = ifm_width - ifm_ls_p0 - ifm_ls_p1 * (ofm_w_quo_wtt_ifm_sw_l - 1);
                        //std::cout << "--------WP 2-------" << std::endl;
                    }
                }

                ofm_w_batch_p0 = (LINE_BUFFER_LEN + padding_left - knl_w) / knl_stride_w + 1;

                if (padding_right == 0) {
                    uint32_t ofm_w_rbatch_no_pad = ifm_sw_p1 >= knl_w ? (ifm_sw_p1 - knl_w) / knl_stride_w + 1 : 0;

                    if (ofm_w_rbatch_no_pad == 0) {
                        ifm_sw_p1 = 0;

                        if (ofm_width <= ofm_w_batch_with_pad_l){
                            ofm_w_batch_p1 = 0;
                            ofm_w_batch_p2 = 0;
                        }
                        else {
                            ofm_w_batch_p1 = (ofm_width - ofm_w_batch_with_pad_l) / ofm_w_batch_no_pad >= 1 ? ofm_w_batch_no_pad : 0;
                            ofm_w_batch_p2 = 0;
                        }
                    }
                    else {
                        if(ofm_width < ofm_w_batch_with_pad_l + ofm_w_rbatch_no_pad) {
                            ofm_w_batch_p1 = ofm_w_rbatch_no_pad;
                            ofm_w_batch_p2 = 0;
                        }
                        else {
                            ofm_w_batch_p1 = (ofm_width - ofm_w_batch_with_pad_l - ofm_w_rbatch_no_pad) / ofm_w_batch_no_pad >= 1 ? ofm_w_batch_no_pad : ofm_w_rbatch_no_pad;
                            ofm_w_batch_p2 = (ofm_width - ofm_w_batch_with_pad_l - ofm_w_rbatch_no_pad) / ofm_w_batch_no_pad >= 1 ? ofm_w_rbatch_no_pad : 0;
                        }
                    }
                }
                else {
                    uint32_t ofm_w_rbatch_with_pad = ifm_sw_p1 + padding_right >= knl_w ? (ifm_sw_p1 + padding_right - knl_w) / knl_stride_w + 1 : 0;
                    if (ofm_w_rbatch_with_pad == 0) {
                        ifm_sw_p1 = 0;

                        if (ofm_width < ofm_w_batch_with_pad_l) {
                            ofm_w_batch_p1 = 0;
                            ofm_w_batch_p2 = 0;
                        }
                        else {
                            ofm_w_batch_p1 = (ofm_width - ofm_w_batch_with_pad_l) / ofm_w_batch_no_pad >= 1 ? ofm_w_batch_no_pad : 0;
                            ofm_w_batch_p2 = 0;
                        }
                    }
                    else {
                        if (ofm_width < ofm_w_batch_with_pad_l + ofm_w_rbatch_with_pad) {
                            ofm_w_batch_p1 = ofm_w_rbatch_with_pad;
                            ofm_w_batch_p2 = 0;
                        }
                        else {
                            ofm_w_batch_p1 = (ofm_width - ofm_w_batch_with_pad_l - ofm_w_rbatch_with_pad) / ofm_w_batch_no_pad >= 1 ? ofm_w_batch_no_pad : ofm_w_rbatch_with_pad;
                            ofm_w_batch_p2 = (ofm_width - ofm_w_batch_with_pad_l - ofm_w_rbatch_with_pad) / ofm_w_batch_no_pad >= 1 ? ofm_w_rbatch_with_pad : 0;
                        }
                    }
                }
            }
        }

        ifm_sw_p0 = LINE_BUFFER_LEN;
        ofm_sw_p0 = ofm_w_batch_p0;
        ofm_sw_p1 = ofm_w_batch_p1;
        ofm_sw_p2 = ofm_w_batch_p2;
    }
    else {
        //std::cout << "********************THIS IS SMALL THAN LINE_BUFFER_LEN IFM_WIDTH REGION****************************" << std::endl;

        ifm_sw_p0 = ifm_width;
        ifm_sw_p1 = 0;
        ifm_ls_p0 = 0;
        ifm_ls_p1 = 0;
        ofm_sw_p0 = ofm_width;
        ofm_sub_width_p0_flag = 0;
        ofm_sw_p1 = 0;
        ofm_sw_p2 = 0;
    }

    //std::cout << "==========ifm_sub_width_p0 is: 0x" << std::hex << ifm_sw_p0 << std::endl
    //          << "ifm_sub_width_p1 is: 0x" << std::hex << ifm_sw_p1 << std::endl
    //          << "ofm_sub_width_p0 is: 0x" << std::hex << ofm_sw_p0 << std::endl
    //          << "ofm_sub_width_p0_flag is: 0x" << std::hex << ofm_sub_width_p0_flag << std::endl
    //          << "ofm_sub_width_p1 is: 0x" << std::hex << ofm_sw_p1 << std::endl
    //          << "ofm_sub_width_p2 is: 0x" << std::hex << ofm_sw_p2 << std::endl
    //          << "ifm_line_stride_p0 is: 0x" << std::hex << ifm_ls_p0 << std::endl
    //          << "ifm_line_stride_p1 is: 0x" << std::hex << ifm_ls_p1 << std::endl
    //          << "=============" << std::endl;
}

void conv_op(void *input, void *weight, void *bias, void *scale, void *output,
             int ifm_width, int ifm_height, int ifm_num, int ofm_num,
             int knl_h, int knl_w, int stride_h, int stride_w,
             int pad_t, int pad_b, int pad_l, int pad_r, int pad_w,
             int activation_type, int lrelu_lmbda, int out_shift)
{
    CHECK(knl_h == knl_w);
    CHECK(knl_h % 2 == 1 && knl_h <= 13);
    CHECK(ifm_width <= 0xfff);
    CHECK(ifm_height <= 0xfff);
    CHECK(ifm_num <= 0x1fff);
    CHECK(ofm_num <= 0x1fff);
    CHECK(stride_h <= 0xf);
    CHECK(stride_w <= 0xf);
    CHECK(pad_l <= 0xf);
    CHECK(pad_r <= 0xf);
    CHECK(pad_t <= 0xf);
    CHECK(pad_b <= 0xf);
    CHECK(out_shift <= 0x1f);

    u32 op_type = 0x0;
    if (knl_h == 13)
    {
        op_type = 1 << 7;
    }
    else
    {
        op_type = 1 << ((knl_h - 1) / 2);
    }

    u32 ifm_size_per_ch = ifm_width * ifm_height;
    //u32 ifm_offset = ifm_size_per_ch;
    u32 ifm_size = ifm_size_per_ch * ifm_num;

    u32 ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
    u32 ofm_height = (ifm_height + pad_t + pad_b - knl_h) / stride_h + 1;
    u32 ofm_size_per_ch = ofm_width * ofm_height;
    //u32 ofm_offset = ofm_size_per_ch;
    u32 ofm_size = ofm_size_per_ch * ofm_num;

    u32 knl_size_per_ch = knl_h * knl_w;
    //u32 knl_offset = knl_size_per_ch;
    u32 knl_size = knl_size_per_ch * ifm_num * ofm_num;

    auto jh7110_obj = std::make_shared<JH7110>();

    u64 dram_input_addr = 0x0;
    jh7110_obj->dram->write_mem(dram_input_addr, input, ifm_size);

    u64 dram_knl_addr = ALIGN_32(dram_input_addr + ifm_size);
    jh7110_obj->dram->write_mem(dram_knl_addr, weight, knl_size);

    u64 dram_bias_scale_addr = ALIGN_32(dram_knl_addr + knl_size);
    for (int i = 0; i < ofm_num; i++) {
        jh7110_obj->dram->write_mem(dram_bias_scale_addr + 2 * i * sizeof(u32), ((u32 *) bias) + i, sizeof(u32));
        jh7110_obj->dram->write_mem(dram_bias_scale_addr + (2 * i + 1) * sizeof(u32), ((u32 *)scale) + i, sizeof(u32));
    }

    u64 dram_output_addr = ALIGN_32(dram_bias_scale_addr + ofm_num * 2 * sizeof(u32));

    //1. do multi ofm channel each run
    if (ALIGN_32(ifm_size) + (ALIGN_32(knl_size_per_ch * ifm_num) + ALIGN_32(2 * sizeof(u32))) <= 2 * 1024 * 1024) {
        //std::cout << "case 1: multi ofm channel each run" << std::endl;
        // load entire ifm
        jh7110_obj->dma->vgrEn[0] = 1;
        jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr & 0xf);
        jh7110_obj->dma->vgrRaddr_h[0] = ((dram_input_addr & (((u64)0xffffffff) << 4)) >> 4);
        jh7110_obj->dma->dramAddrStep[0] = ifm_size;
        jh7110_obj->dma->burstDataLen[0] = ifm_size;
        jh7110_obj->dma->burstDataTimes[0] = 1;
        jh7110_obj->dma->writeDataLen[0] = ifm_size;
        jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
        jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & 0x1f);
        jh7110_obj->dma->ramWaddrStep[0] = ifm_size;
        jh7110_obj->dma->mode[0] = 0x0;
        jh7110_obj->dma->scale[0] = 0;
        jh7110_obj->dma->padZero[0] = 0;
        jh7110_obj->dma->vgrEn[1] = 0;
        jh7110_obj->dma->convDmaEn = 1;
        jh7110_obj->dma->Run();

        int ofm_num_each_run = 1;
        for (int i = 1; i <= ofm_num; i++) {
            if (ALIGN_32(ifm_size) + ALIGN_32(knl_size_per_ch * ifm_num * i) + ALIGN_32(2 * sizeof(u32) * i) > 2 * 1024 * 1024) {
                ofm_num_each_run = i - 1;
                break;
            }
            else if (i >= ofm_num) {
                ofm_num_each_run = ofm_num;
                break;
            }
        }

        u32 padding_idc = 0;
        u32 padding_size = 0;
        if(pad_l){
            padding_size |= ((pad_l & 0xf) << 0);
            padding_idc |= 1;
        }
        if(pad_r){
            padding_size |= ((pad_r & 0xf) << 8);
            padding_idc |= 2;
        }
        if(pad_t){
            padding_size |= ((pad_t & 0xf) << 16);
            padding_idc |= 4;
        }
        if(pad_b){
            padding_size |= ((pad_b & 0xf) << 24);
            padding_idc |= 8;
        }

        int ofm_num_cur_run;
        int ofm_num_remain;
        for (int i = 0; i < ofm_num; i += ofm_num_cur_run) {
            ofm_num_remain = ofm_num - i;
            ofm_num_cur_run = (ofm_num_remain > ofm_num_each_run) ? ofm_num_each_run : ofm_num_remain;
            //knl dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = ((dram_knl_addr + knl_size_per_ch * ifm_num * i) & 0xf);
            jh7110_obj->dma->vgrRaddr_h[0] = (((dram_knl_addr + knl_size_per_ch * ifm_num * i) & (((u64)0xffffffff) << 4)) >> 4);
            jh7110_obj->dma->dramAddrStep[0] = knl_size_per_ch * ifm_num * ofm_num_cur_run;
            jh7110_obj->dma->burstDataLen[0] = knl_size_per_ch * ifm_num * ofm_num_cur_run;
            jh7110_obj->dma->burstDataTimes[0] = 1;
            jh7110_obj->dma->writeDataLen[0] = knl_size_per_ch * ifm_num * ofm_num_cur_run;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size)) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size)) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = knl_size_per_ch * ifm_num * ofm_num_cur_run;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;

            //bias, scale dma
            jh7110_obj->dma->vgrEn[1] = 1;
            jh7110_obj->dma->vgrRaddr_l[1] = ((dram_bias_scale_addr + sizeof(u32) * 2 * i) & 0xf);
            jh7110_obj->dma->vgrRaddr_h[1] = (((dram_bias_scale_addr + sizeof(u32) * 2 * i) & (((u64)0xffffffff) << 4)) >> 4);
            jh7110_obj->dma->dramAddrStep[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->burstDataLen[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->burstDataTimes[1] = 1;
            jh7110_obj->dma->writeDataLen[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->ramWaddr[1] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size) + ALIGN_32(knl_size_per_ch * ifm_num * ofm_num_cur_run)) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[1] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size) + ALIGN_32(knl_size_per_ch * ifm_num * ofm_num_cur_run)) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->mode[1] = 0x0;
            jh7110_obj->dma->scale[1] = 0;
            jh7110_obj->dma->padZero[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            u32 ifm_sub_width_p0;
            u32 ifm_sub_width_p1;
            u32 ifm_line_stride_p0;
            u32 ifm_line_stride_p1;
            u32 ofm_sub_width_p0;
            u32 ofm_sub_width_p1;
            u32 ofm_sub_width_p2;
            u32 ofm_sub_width_p0_flag;
            ifm_param_gen(ifm_width, ofm_width, knl_w, stride_w, pad_l, pad_r,
                          ifm_sub_width_p0, ifm_sub_width_p1, ifm_line_stride_p0, ifm_line_stride_p1,
                          ofm_sub_width_p0, ofm_sub_width_p1, ofm_sub_width_p2, ofm_sub_width_p0_flag);

            jh7110_obj->convolutionCore->irq_status = false;
            jh7110_obj->convolutionCore->kernel_stride = (stride_w << 16) + stride_h;
            jh7110_obj->convolutionCore->ifm_line_stride = ((ifm_line_stride_p1 & 0x3f) << 6) + (ifm_line_stride_p0 & 0x3f);
            jh7110_obj->convolutionCore->ifm_width = (ifm_width & 0xfff) + ((jh7110_obj->convolutionCore->ifm_line_stride & 0xfff) << 16);
            jh7110_obj->convolutionCore->ifm_height = ifm_height;
            jh7110_obj->convolutionCore->ofm_width = ofm_width;
            jh7110_obj->convolutionCore->ofm_height = ofm_height;
            jh7110_obj->convolutionCore->ifm_num = ifm_num;
            jh7110_obj->convolutionCore->ofm_num = ofm_num_cur_run;
            jh7110_obj->convolutionCore->padding_idc = padding_idc;
            jh7110_obj->convolutionCore->padding_word = 0;
            jh7110_obj->convolutionCore->padding_size = padding_size;
            jh7110_obj->convolutionCore->op_type = op_type;
            if(activation_type == 0 || activation_type == 1)
            {
                jh7110_obj->convolutionCore->conv_detail = activation_type;
            }
            else if(activation_type == 2)
            {
                jh7110_obj->convolutionCore->conv_detail = 0x4 | (lrelu_lmbda << 8);
            }
            jh7110_obj->convolutionCore->ifm_start_L = ((0x0 >> 5) & 0xffffffff);
            jh7110_obj->convolutionCore->ifm_start_m = (((0x0 >> 5) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->ifm_offset = (ifm_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
            jh7110_obj->convolutionCore->knl_start_L = ((ALIGN_32(ifm_size) >> 5) & 0xffffffff);
            jh7110_obj->convolutionCore->knl_start_m = (((ALIGN_32(ifm_size) >> 5) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->knl_offset = (knl_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
            jh7110_obj->convolutionCore->knl_size = knl_size_per_ch;
            jh7110_obj->convolutionCore->ofm_bias_start_L = (((ALIGN_32(ifm_size) + ALIGN_32(knl_size_per_ch * ifm_num * ofm_num_cur_run)) >> 5) & 0xffffffff);
            jh7110_obj->convolutionCore->ofm_bias_start_m = (((((ALIGN_32(ifm_size) + ALIGN_32(knl_size_per_ch * ifm_num * ofm_num_cur_run)) >> 5) & ((u64)0xf << 32)) >> 32) & 0x0000000f) + ((0x0 & 0x0000001f) << 16);
            jh7110_obj->convolutionCore->ofm_start_L = ((dram_output_addr + i * ofm_size_per_ch) & 0xffffffff);
            jh7110_obj->convolutionCore->ofm_start_m = (((dram_output_addr + i * ofm_size_per_ch) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->ofm_offset = ofm_size_per_ch;
            jh7110_obj->convolutionCore->out_shift = out_shift;
            //TODO: lut_bypass
            jh7110_obj->convolutionCore->lut_bypass = 1;
            jh7110_obj->convolutionCore->ifm_sub_width = ((ifm_sub_width_p1 & 0x3f) << 6) + ifm_sub_width_p0;
            jh7110_obj->convolutionCore->ofm_sub_width_flag = ofm_sub_width_p0_flag;
            jh7110_obj->convolutionCore->ofm_sub_width = ofm_sub_width_p0 + ((ofm_sub_width_p1 & 0x3ff) << 10) + ((ofm_sub_width_p2 & 0x3ff) << 20);
            jh7110_obj->convolutionCore->cvnn_start = true;
            jh7110_obj->convolutionCore->Run();
        }
    }
    //2. do multi ofm line each run
    else if (ALIGN_32(ifm_width * ifm_num * knl_h) + (ALIGN_32(knl_size_per_ch * ifm_num) + ALIGN_32(2 * sizeof(u32))) <= 2 * 1024 * 1024) {
        //std::cout << "case 2: multi ofm line each run" << std::endl;
        int line_num_each_run = knl_h;
        for (int i = knl_h; i <= ifm_height; i++) {
            if (ALIGN_32(ifm_width * ifm_num * i) + ALIGN_32(knl_size_per_ch * ifm_num) + ALIGN_32(2 * sizeof(u32)) > 2 * 1024 * 1024) {
                line_num_each_run = i - 1;
                break;
            }
            else if (i >= ifm_height) {
                line_num_each_run = ifm_height;
                break;
            }
        }
        int line_num_cur_run;
        int line_num_reload;
        int line_idx = 0;
        int ofm_line_idx = 0;
        for (line_idx = 0; line_idx < ifm_height; line_idx += (line_num_cur_run - line_num_reload)) {
            line_num_cur_run = ((line_idx + line_num_each_run) > ifm_height) ? (ifm_height - line_idx): line_num_each_run;
            line_num_reload = ((line_idx + line_num_cur_run) == ifm_height) ? 0 : line_num_cur_run - (((line_num_cur_run + pad_t * (line_idx == 0) - knl_h) / stride_h + 1) * stride_h - pad_t * (line_idx == 0));

            if ((line_num_cur_run + pad_t * (line_idx == 0) + pad_b * ((line_idx + line_num_cur_run) == ifm_height)) < knl_h) {
                continue;
            }

            //ifm lines dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + line_idx * ifm_width) & 0xf;
            jh7110_obj->dma->vgrRaddr_h[0] = (((dram_input_addr + line_idx * ifm_width) & (((u64)0xffffffff) << 4)) >> 4);
            jh7110_obj->dma->dramAddrStep[0] = ifm_size_per_ch;
            jh7110_obj->dma->burstDataLen[0] = ifm_width * line_num_cur_run;
            jh7110_obj->dma->burstDataTimes[0] = ifm_num;
            jh7110_obj->dma->writeDataLen[0] = ifm_width * line_num_cur_run;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = ifm_width * line_num_cur_run;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;
            jh7110_obj->dma->vgrEn[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            int sram_knl_addr = ALIGN_32(ifm_width * line_num_cur_run * ifm_num);
            int sram_bias_scale_addr = ALIGN_32(ifm_width * line_num_cur_run * ifm_num) + ALIGN_32(ifm_num * knl_size_per_ch);
            for (int i = 0; i < ofm_num; i++) {
                //knl dma
                jh7110_obj->dma->vgrEn[0] = 1;
                jh7110_obj->dma->vgrRaddr_l[0] = (dram_knl_addr + i * ifm_num * knl_size_per_ch) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[0] = ((dram_knl_addr + i * ifm_num * knl_size_per_ch) & (((u64)0xffffffff) << 4)) >> 4;
                jh7110_obj->dma->dramAddrStep[0] = ifm_num * knl_size_per_ch;
                jh7110_obj->dma->burstDataLen[0] = ifm_num * knl_size_per_ch;
                jh7110_obj->dma->burstDataTimes[0] = 1;
                jh7110_obj->dma->writeDataLen[0] = ifm_num * knl_size_per_ch;
                jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + sram_knl_addr) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + sram_knl_addr) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[0] = ifm_num * knl_size_per_ch;
                jh7110_obj->dma->mode[0] = 0x0;
                jh7110_obj->dma->scale[0] = 0;
                jh7110_obj->dma->padZero[0] = 0;
                //bias scale dma
                jh7110_obj->dma->vgrEn[1] = 1;
                jh7110_obj->dma->vgrRaddr_l[1] = (dram_bias_scale_addr + i * 2 * sizeof(u32)) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[1] = (((dram_bias_scale_addr + i * 2 * sizeof(u32)) & (((u64)0xffffffff) << 4)) >> 4);
                jh7110_obj->dma->dramAddrStep[1] = 2 * sizeof(u32);
                jh7110_obj->dma->burstDataLen[1] = 2 * sizeof(u32);
                jh7110_obj->dma->burstDataTimes[1] = 1;
                jh7110_obj->dma->writeDataLen[1] = 2 * sizeof(u32);
                jh7110_obj->dma->ramWaddr[1] = ((jh7110_obj->convCoreStartAddr + sram_bias_scale_addr) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[1] = ((jh7110_obj->convCoreStartAddr + sram_bias_scale_addr) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[1] = 2 * sizeof(u32);
                jh7110_obj->dma->mode[1] = 0x0;
                jh7110_obj->dma->scale[1] = 0;
                jh7110_obj->dma->padZero[1] = 0;
                jh7110_obj->dma->convDmaEn = 1;
                jh7110_obj->dma->Run();

                //do conv
                u32 ifm_sub_width_p0;
                u32 ifm_sub_width_p1;
                u32 ifm_line_stride_p0;
                u32 ifm_line_stride_p1;
                u32 ofm_sub_width_p0;
                u32 ofm_sub_width_p1;
                u32 ofm_sub_width_p2;
                u32 ofm_sub_width_p0_flag;
                ifm_param_gen(ifm_width, ofm_width, knl_w, stride_w, pad_l, pad_r,
                              ifm_sub_width_p0, ifm_sub_width_p1, ifm_line_stride_p0, ifm_line_stride_p1,
                              ofm_sub_width_p0, ofm_sub_width_p1, ofm_sub_width_p2, ofm_sub_width_p0_flag);

                jh7110_obj->convolutionCore->irq_status = false;
                jh7110_obj->convolutionCore->kernel_stride = (stride_w << 16) + stride_h;
                //TODO: ifm_line_stride
                jh7110_obj->convolutionCore->ifm_line_stride = ((ifm_line_stride_p1 & 0x3f) << 6) + (ifm_line_stride_p0 & 0x3f);
                jh7110_obj->convolutionCore->ifm_width = (ifm_width & 0xfff) + ((jh7110_obj->convolutionCore->ifm_line_stride & 0xfff) << 16);
                jh7110_obj->convolutionCore->ifm_height = line_num_cur_run;
                jh7110_obj->convolutionCore->ofm_width = ofm_width;
                jh7110_obj->convolutionCore->ofm_height = (line_num_cur_run +
                                        pad_t * (line_idx == 0) +
                                        pad_b * ((line_idx + line_num_cur_run) == ifm_height) -
                                        knl_h) / stride_h + 1;
                jh7110_obj->convolutionCore->ifm_num = ifm_num;
                jh7110_obj->convolutionCore->ofm_num = 1;

                u32 padding_size = 0;
                u32 padding_idc = 0;
                if(pad_l){
                    padding_size |= ((pad_l & 0xf) << 0);
                    padding_idc |= 1;
                }
                if(pad_r){
                    padding_size |= ((pad_r & 0xf) << 8);
                    padding_idc |= 2;
                }
                if(pad_t){
                    padding_size |= (((pad_t * (line_idx == 0)) & 0xf) << 16);
                    padding_idc |= (4 * (line_idx == 0));
                }
                if(pad_b){
                    padding_size |= (((pad_b * (((line_idx + line_num_cur_run) == ifm_height))) & 0xf) << 24);
                    padding_idc |= (8 * (line_idx + line_num_cur_run == ifm_height));
                }

                jh7110_obj->convolutionCore->padding_idc = padding_idc;
                jh7110_obj->convolutionCore->padding_word = 0;
                jh7110_obj->convolutionCore->padding_size = padding_size;
                jh7110_obj->convolutionCore->op_type = op_type;
                if(activation_type == 0 || activation_type == 1)
                {
                    jh7110_obj->convolutionCore->conv_detail = activation_type;
                }
                else if(activation_type == 2)
                {
                    jh7110_obj->convolutionCore->conv_detail = 0x4 | (lrelu_lmbda << 8);
                }
                jh7110_obj->convolutionCore->ifm_start_L = ((0x0 >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->ifm_start_m = (((0x0 >> 5) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->ifm_offset = ((ifm_width * line_num_cur_run) & 0x00ffffff) + ((0x0 & 0x1f) << 24);
                jh7110_obj->convolutionCore->knl_start_L = ((sram_knl_addr >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->knl_start_m = (((sram_knl_addr >> 5) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->knl_offset = (knl_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
                jh7110_obj->convolutionCore->knl_size = knl_size_per_ch;
                jh7110_obj->convolutionCore->ofm_bias_start_L = ((sram_bias_scale_addr >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->ofm_bias_start_m = ((((sram_bias_scale_addr >> 5) & ((u64)0xf << 32)) >> 32) & 0x0000000f) + ((0x0 & 0x0000001f) << 16);
                jh7110_obj->convolutionCore->ofm_start_L = ((dram_output_addr + i * ofm_width * ofm_height + ofm_line_idx * ofm_width) & 0xffffffff);
                jh7110_obj->convolutionCore->ofm_start_m = (((dram_output_addr + i * ofm_width * ofm_height + ofm_line_idx * ofm_width) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->ofm_offset = ofm_size_per_ch;
                jh7110_obj->convolutionCore->out_shift = out_shift;
                //TODO: lut_bypass
                jh7110_obj->convolutionCore->lut_bypass = 1;
                jh7110_obj->convolutionCore->ifm_sub_width = ((ifm_sub_width_p1 & 0x3f) << 6) + ifm_sub_width_p0;
                jh7110_obj->convolutionCore->ofm_sub_width_flag = ofm_sub_width_p0_flag;
                jh7110_obj->convolutionCore->ofm_sub_width= ofm_sub_width_p0 + ((ofm_sub_width_p1 & 0x3ff) << 10) + ((ofm_sub_width_p2 & 0x3ff) << 20);

                jh7110_obj->convolutionCore->cvnn_start = true;
                jh7110_obj->convolutionCore->Run();
            }
            ofm_line_idx += jh7110_obj->convolutionCore->ofm_height;
        }
    }

    //NOTE: not used by now, since conv path don't support ofm line stride
    //3. do tile division

    //TODO
    //4. divide ifm channel and do multi ofm line each run
    else {
        //std::cout << "case 4: not supported by now" << std::endl;
        std::exit(-1);
    }

    jh7110_obj->dram->read_mem(output, dram_output_addr, ofm_size);
    return;
}

void dw_conv_op(void *input, void *weight, void *bias, void *scale, void *output,
                int ifm_width, int ifm_height, int ifm_num,
                int knl_h, int knl_w, int stride_h, int stride_w,
                int pad_t, int pad_b, int pad_l, int pad_r, int pad_w,
                int activation_type, int lrelu_lmbda, int out_shift)
{
    CHECK(knl_h == knl_w);
    CHECK(knl_h % 2 == 1 && knl_h <= 13);
    CHECK(ifm_width <= 0xfff);
    CHECK(ifm_height <= 0xfff);
    CHECK(ifm_num <= 0x1fff);
    CHECK(stride_h <= 0xf);
    CHECK(stride_w <= 0xf);
    CHECK(pad_l <= 0xf);
    CHECK(pad_r <= 0xf);
    CHECK(pad_t <= 0xf);
    CHECK(pad_b <= 0xf);
    CHECK(out_shift <= 0x1f);

    u32 op_type = 0x0;
    if (knl_h == 13)
    {
        op_type = 1 << 7;
    }
    else
    {
        op_type = 1 << ((knl_h - 1) / 2);
    }

    int ofm_num = ifm_num;
    u32 ifm_size_per_ch = ifm_width * ifm_height;
    //u32 ifm_offset = ifm_size_per_ch;
    u32 ifm_size = ifm_size_per_ch * ifm_num;

    u32 ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
    u32 ofm_height = (ifm_height + pad_t + pad_b - knl_h) / stride_h + 1;
    u32 ofm_size_per_ch = ofm_width * ofm_height;
    //u32 ofm_offset = ofm_size_per_ch;
    u32 ofm_size = ofm_size_per_ch * ofm_num;

    u32 knl_size_per_ch = knl_h * knl_w;
    //u32 knl_offset = knl_size_per_ch;
    u32 knl_size = knl_size_per_ch * ofm_num;

    auto jh7110_obj = std::make_shared<JH7110>();

    u64 dram_input_addr = 0x0;
    jh7110_obj->dram->write_mem(dram_input_addr, input, ifm_size);

    u64 dram_knl_addr = ALIGN_32(dram_input_addr + ifm_size);
    jh7110_obj->dram->write_mem(dram_knl_addr, weight, knl_size);

    u64 dram_bias_scale_addr = ALIGN_32(dram_knl_addr + knl_size);
    for (int i = 0; i < ofm_num; i++) {
        jh7110_obj->dram->write_mem(dram_bias_scale_addr + 2 * i * sizeof(u32), ((u32 *) bias) + i, sizeof(u32));
        jh7110_obj->dram->write_mem(dram_bias_scale_addr + (2 * i + 1) * sizeof(u32), ((u32 *)scale) + i, sizeof(u32));
    }

    u64 dram_output_addr = ALIGN_32(dram_bias_scale_addr + ofm_num * 2 * sizeof(u32));

    //1. do multi ofm channel each run
    if (ALIGN_32(ifm_size_per_ch) + (ALIGN_32(knl_size_per_ch) + ALIGN_32(2 * sizeof(u32))) <= 2 * 1024 * 1024) {
        //std::cout << "case 1: multi ofm channel each run" << std::endl;
        int ofm_num_each_run = 1;
        for (int i = 1; i <= ofm_num; i++) {
            if (ALIGN_32(ifm_size_per_ch * i) + ALIGN_32(knl_size_per_ch * i) + ALIGN_32(2 * sizeof(u32) * i) > 2 * 1024 * 1024) {
                ofm_num_each_run = i - 1;
                break;
            }
            else if (i >= ofm_num) {
                ofm_num_each_run = ofm_num;
                break;
            }
        }

        u32 padding_idc = 0;
        u32 padding_size = 0;
        if(pad_l){
            padding_size |= ((pad_l & 0xf) << 0);
            padding_idc |= 1;
        }
        if(pad_r){
            padding_size |= ((pad_r & 0xf) << 8);
            padding_idc |= 2;
        }
        if(pad_t){
            padding_size |= ((pad_t & 0xf) << 16);
            padding_idc |= 4;
        }
        if(pad_b){
            padding_size |= ((pad_b & 0xf) << 24);
            padding_idc |= 8;
        }

        int ofm_num_cur_run;
        int ofm_num_remain;
        for (int i = 0; i < ofm_num; i += ofm_num_cur_run) {
            ofm_num_remain = ofm_num - i;
            ofm_num_cur_run = (ofm_num_remain > ofm_num_each_run) ? ofm_num_each_run : ofm_num_remain;

            //ifm dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = ((dram_input_addr + i * ifm_size_per_ch)& 0xf);
            jh7110_obj->dma->vgrRaddr_h[0] = ((dram_input_addr + i * ifm_size_per_ch) & (((u64)0xffffffff) << 4)) >> 4;
            jh7110_obj->dma->dramAddrStep[0] = ifm_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->burstDataLen[0] = ifm_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->burstDataTimes[0] = 1;
            jh7110_obj->dma->writeDataLen[0] = ifm_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = ifm_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;
            jh7110_obj->dma->vgrEn[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            //knl dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = ((dram_knl_addr + knl_size_per_ch * i) & 0xf);
            jh7110_obj->dma->vgrRaddr_h[0] = ((dram_knl_addr + knl_size_per_ch * i) & (((u64)0xffffffff) << 4)) >> 4;
            jh7110_obj->dma->dramAddrStep[0] = knl_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->burstDataLen[0] = knl_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->burstDataTimes[0] = 1;
            jh7110_obj->dma->writeDataLen[0] = knl_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size_per_ch * ofm_num_cur_run)) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size_per_ch * ofm_num_cur_run)) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = knl_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;

            //bias, scale dma
            jh7110_obj->dma->vgrEn[1] = 1;
            jh7110_obj->dma->vgrRaddr_l[1] = ((dram_bias_scale_addr + sizeof(u32) * 2 * i) & 0xf);
            jh7110_obj->dma->vgrRaddr_h[1] = ((dram_bias_scale_addr + sizeof(u32) * 2 * i) & (((u64)0xffffffff) << 4)) >> 4;
            jh7110_obj->dma->dramAddrStep[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->burstDataLen[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->burstDataTimes[1] = 1;
            jh7110_obj->dma->writeDataLen[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->ramWaddr[1] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) + ALIGN_32(knl_size_per_ch * ofm_num_cur_run)) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[1] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) + ALIGN_32(knl_size_per_ch * ofm_num_cur_run)) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->mode[1] = 0x0;
            jh7110_obj->dma->scale[1] = 0;
            jh7110_obj->dma->padZero[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            u32 ifm_sub_width_p0;
            u32 ifm_sub_width_p1;
            u32 ifm_line_stride_p0;
            u32 ifm_line_stride_p1;
            u32 ofm_sub_width_p0;
            u32 ofm_sub_width_p1;
            u32 ofm_sub_width_p2;
            u32 ofm_sub_width_p0_flag;
            ifm_param_gen(ifm_width, ofm_width, knl_w, stride_w, pad_l, pad_r,
                          ifm_sub_width_p0, ifm_sub_width_p1, ifm_line_stride_p0, ifm_line_stride_p1,
                          ofm_sub_width_p0, ofm_sub_width_p1, ofm_sub_width_p2, ofm_sub_width_p0_flag);
            jh7110_obj->convolutionCore->irq_status = false;
            jh7110_obj->convolutionCore->kernel_stride = (stride_w << 16) + stride_h;
            jh7110_obj->convolutionCore->ifm_line_stride = ((ifm_line_stride_p1 & 0x3f) << 6) + (ifm_line_stride_p0 & 0x3f);
            jh7110_obj->convolutionCore->ifm_width = (ifm_width & 0xfff) + ((jh7110_obj->convolutionCore->ifm_line_stride & 0xfff) << 16);
            jh7110_obj->convolutionCore->ifm_height = ifm_height;
            jh7110_obj->convolutionCore->ofm_width = ofm_width;
            jh7110_obj->convolutionCore->ofm_height = ofm_height;
            jh7110_obj->convolutionCore->ifm_num = ofm_num_cur_run;
            jh7110_obj->convolutionCore->ofm_num = ofm_num_cur_run;
            jh7110_obj->convolutionCore->padding_idc = padding_idc;
            jh7110_obj->convolutionCore->padding_word = 0;
            jh7110_obj->convolutionCore->padding_size = padding_size;
            jh7110_obj->convolutionCore->op_type = op_type;
            if(activation_type == 0 || activation_type == 1)
            {
                jh7110_obj->convolutionCore->conv_detail = activation_type;
            }
            else if(activation_type == 2)
            {
                jh7110_obj->convolutionCore->conv_detail = 0x4 | (lrelu_lmbda << 8);
            }
            jh7110_obj->convolutionCore->conv_detail |= 0x2;
            jh7110_obj->convolutionCore->ifm_start_L = ((0x0 >>5) & 0xffffffff);
            jh7110_obj->convolutionCore->ifm_start_m = (((0x0 >>5) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->ifm_offset = (ifm_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
            jh7110_obj->convolutionCore->knl_start_L = ((ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) >> 5) & 0xffffffff);
            jh7110_obj->convolutionCore->knl_start_m = (((ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) >> 5) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->knl_offset = (knl_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
            jh7110_obj->convolutionCore->knl_size = knl_size_per_ch;
            jh7110_obj->convolutionCore->ofm_bias_start_L = (((ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) + ALIGN_32(knl_size_per_ch * ofm_num_cur_run)) >> 5) & 0xffffffff);
            jh7110_obj->convolutionCore->ofm_bias_start_m = (((((ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) + ALIGN_32(knl_size_per_ch * ofm_num_cur_run)) >> 5) & ((u64)0xf << 32)) >> 32) & 0x0000000f) + ((0x0 & 0x0000001f) << 16);
            jh7110_obj->convolutionCore->ofm_start_L = ((dram_output_addr + i * ofm_width * ofm_height) & 0xffffffff);
            jh7110_obj->convolutionCore->ofm_start_m = (((dram_output_addr + i * ofm_width * ofm_height) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->ofm_offset = ofm_size_per_ch;
            jh7110_obj->convolutionCore->out_shift = out_shift;
            //TODO: lut_bypass
            jh7110_obj->convolutionCore->lut_bypass = 1;
            jh7110_obj->convolutionCore->ifm_sub_width = ((ifm_sub_width_p1 & 0x3f) << 6) + ifm_sub_width_p0;
            jh7110_obj->convolutionCore->ofm_sub_width_flag = ofm_sub_width_p0_flag;
            jh7110_obj->convolutionCore->ofm_sub_width = ofm_sub_width_p0 + ((ofm_sub_width_p1 & 0x3ff) << 10) + ((ofm_sub_width_p2 & 0x3ff) << 20);
            jh7110_obj->convolutionCore->cvnn_start = true;
            jh7110_obj->convolutionCore->Run();
        }
    }
    //2. do multi ofm line each run
    else if (ALIGN_32(ifm_width * knl_h) + (ALIGN_32(knl_size_per_ch) + ALIGN_32(2 * sizeof(u32))) <= 2 * 1024 * 1024) {
        //std::cout << "case 2: multi ofm line each run" << std::endl;
        int line_num_each_run = knl_h;
        for (int i = knl_h; i <= ifm_height; i++) {
            if (ALIGN_32(ifm_width * i) + ALIGN_32(knl_size_per_ch) + ALIGN_32(2 * sizeof(u32)) > 2 * 1024 * 1024) {
                line_num_each_run = i - 1;
                break;
            }
            else if (i >= ifm_height) {
                line_num_each_run = ifm_height;
                break;
            }
        }
        for (int i = 0; i < ofm_num; i++) {
            int line_num_cur_run;
            int line_num_reload;
            int line_idx = 0;
            int ofm_line_idx = 0;
            for (line_idx = 0; line_idx < ifm_height; line_idx += (line_num_cur_run - line_num_reload)) {
                line_num_cur_run = ((line_idx + line_num_each_run) > ifm_height) ? (ifm_height - line_idx): line_num_each_run;
                line_num_reload = ((line_idx + line_num_cur_run) == ifm_height) ? 0 : line_num_cur_run - (((line_num_cur_run + pad_t * (line_idx == 0) - knl_h) / stride_h + 1) * stride_h - pad_t * (line_idx == 0));

                if ((line_num_cur_run + pad_t * (line_idx == 0) + pad_b * ((line_idx + line_num_cur_run) == ifm_height)) < knl_h) {
                    continue;
                }

                //ifm lines dma
                jh7110_obj->dma->vgrEn[0] = 1;
                jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + i * ifm_size_per_ch + line_idx * ifm_width) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[0] = ((dram_input_addr + i * ifm_size_per_ch + line_idx * ifm_width) & (((u64)0xffffffff) << 4)) >> 4;
                jh7110_obj->dma->dramAddrStep[0] = ifm_width * line_num_cur_run;
                jh7110_obj->dma->burstDataLen[0] = ifm_width * line_num_cur_run;
                jh7110_obj->dma->burstDataTimes[0] = 1;
                jh7110_obj->dma->writeDataLen[0] = ifm_width * line_num_cur_run;
                jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[0] = ifm_width * line_num_cur_run;
                jh7110_obj->dma->mode[0] = 0x0;
                jh7110_obj->dma->scale[0] = 0;
                jh7110_obj->dma->padZero[0] = 0;
                jh7110_obj->dma->vgrEn[1] = 0;
                jh7110_obj->dma->convDmaEn = 1;
                jh7110_obj->dma->Run();

                int sram_knl_addr = ALIGN_32(ifm_width * line_num_cur_run);
                int sram_bias_scale_addr = ALIGN_32(ifm_width * line_num_cur_run) + ALIGN_32(knl_size_per_ch);
                    //knl dma
                jh7110_obj->dma->vgrEn[0] = 1;
                jh7110_obj->dma->vgrRaddr_l[0] = (dram_knl_addr + i * knl_size_per_ch) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[0] = ((dram_knl_addr + i * knl_size_per_ch) & (((u64)0xffffffff) << 4)) >> 4;
                jh7110_obj->dma->dramAddrStep[0] = knl_size_per_ch;
                jh7110_obj->dma->burstDataLen[0] = knl_size_per_ch;
                jh7110_obj->dma->burstDataTimes[0] = 1;
                jh7110_obj->dma->writeDataLen[0] = knl_size_per_ch;
                jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + sram_knl_addr) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + sram_knl_addr) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[0] = knl_size_per_ch;
                jh7110_obj->dma->mode[0] = 0x0;
                jh7110_obj->dma->scale[0] = 0;
                jh7110_obj->dma->padZero[0] = 0;
                //bias scale dma
                jh7110_obj->dma->vgrEn[1] = 1;
                jh7110_obj->dma->vgrRaddr_l[1] = (dram_bias_scale_addr + i * 2 * sizeof(u32)) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[1] = ((dram_bias_scale_addr + i * 2 * sizeof(u32)) & (((u64)0xffffffff) << 4)) >> 4;
                jh7110_obj->dma->dramAddrStep[1] = 2 * sizeof(u32);
                jh7110_obj->dma->burstDataLen[1] = 2 * sizeof(u32);
                jh7110_obj->dma->burstDataTimes[1] = 1;
                jh7110_obj->dma->writeDataLen[1] = 2 * sizeof(u32);
                jh7110_obj->dma->ramWaddr[1] = ((jh7110_obj->convCoreStartAddr + sram_bias_scale_addr) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[1] = ((jh7110_obj->convCoreStartAddr + sram_bias_scale_addr) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[1] = 2 * sizeof(u32);
                jh7110_obj->dma->mode[1] = 0x0;
                jh7110_obj->dma->scale[1] = 0;
                jh7110_obj->dma->padZero[1] = 0;
                jh7110_obj->dma->convDmaEn = 1;
                jh7110_obj->dma->Run();

                //do conv
                u32 ifm_sub_width_p0;
                u32 ifm_sub_width_p1;
                u32 ifm_line_stride_p0;
                u32 ifm_line_stride_p1;
                u32 ofm_sub_width_p0;
                u32 ofm_sub_width_p1;
                u32 ofm_sub_width_p2;
                u32 ofm_sub_width_p0_flag;
                ifm_param_gen(ifm_width, ofm_width, knl_w, stride_w, pad_l, pad_r,
                              ifm_sub_width_p0, ifm_sub_width_p1, ifm_line_stride_p0, ifm_line_stride_p1,
                              ofm_sub_width_p0, ofm_sub_width_p1, ofm_sub_width_p2, ofm_sub_width_p0_flag);
                jh7110_obj->convolutionCore->irq_status = false;
                jh7110_obj->convolutionCore->kernel_stride = (stride_w << 16) + stride_h;
                jh7110_obj->convolutionCore->ifm_line_stride = ((ifm_line_stride_p1 & 0x3f) << 6) + (ifm_line_stride_p0 & 0x3f);
                jh7110_obj->convolutionCore->ifm_width = (ifm_width & 0xfff) + ((jh7110_obj->convolutionCore->ifm_line_stride & 0xfff) << 16);
                jh7110_obj->convolutionCore->ifm_height = line_num_cur_run;
                jh7110_obj->convolutionCore->ofm_width = ofm_width;
                jh7110_obj->convolutionCore->ofm_height = (line_num_cur_run +
                                                           pad_t * (line_idx == 0) +
                                                           pad_b * ((line_idx + line_num_cur_run) == ifm_height) -
                                                           knl_h) / stride_h + 1;
                jh7110_obj->convolutionCore->ifm_num = 1;
                jh7110_obj->convolutionCore->ofm_num = 1;

                u32 padding_size = 0;
                u32 padding_idc = 0;
                if(pad_l){
                    padding_size |= ((pad_l & 0xf) << 0);
                    padding_idc |= 1;
                }
                if(pad_r){
                    padding_size |= ((pad_r & 0xf) << 8);
                    padding_idc |= 2;
                }
                if(pad_t){
                    padding_size |= (((pad_t * (line_idx == 0)) & 0xf) << 16);
                    padding_idc |= (4 * (line_idx == 0));
                }
                if(pad_b){
                    padding_size |= (((pad_b * (((line_idx + line_num_cur_run) == ifm_height))) & 0xf) << 24);
                    padding_idc |= (8 * (line_idx + line_num_cur_run == ifm_height));
                }

                jh7110_obj->convolutionCore->padding_idc = padding_idc;
                jh7110_obj->convolutionCore->padding_word = 0;
                jh7110_obj->convolutionCore->padding_size = padding_size;
                jh7110_obj->convolutionCore->op_type = op_type;
                if(activation_type == 0 || activation_type == 1)
                {
                    jh7110_obj->convolutionCore->conv_detail = activation_type;
                }
                else if(activation_type == 2)
                {
                    jh7110_obj->convolutionCore->conv_detail = 0x4 | (lrelu_lmbda << 8);
                }
                jh7110_obj->convolutionCore->conv_detail |= 0x2;
                jh7110_obj->convolutionCore->ifm_start_L = ((0x0 >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->ifm_start_m = (((0x0 >> 5) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->ifm_offset = ((ifm_width * line_num_cur_run) & 0x00ffffff) + ((0x0 & 0x1f) << 24);
                jh7110_obj->convolutionCore->knl_start_L = ((sram_knl_addr >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->knl_start_m = (((sram_knl_addr >> 5) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->knl_offset = (knl_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
                jh7110_obj->convolutionCore->knl_size = knl_size_per_ch;
                jh7110_obj->convolutionCore->ofm_bias_start_L = ((sram_bias_scale_addr >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->ofm_bias_start_m = ((((sram_bias_scale_addr >> 5) & ((u64)0xf << 32)) >> 32) & 0x0000000f) + ((0x0 & 0x0000001f) << 16);
                jh7110_obj->convolutionCore->ofm_start_L = ((dram_output_addr + i * ofm_width * ofm_height + ofm_line_idx * ofm_width) & 0xffffffff);
                jh7110_obj->convolutionCore->ofm_start_m = (((dram_output_addr + i * ofm_width * ofm_height + ofm_line_idx * ofm_width) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->ofm_offset = ofm_size_per_ch;
                jh7110_obj->convolutionCore->out_shift = out_shift;
                //TODO: lut_bypass
                jh7110_obj->convolutionCore->lut_bypass = 1;
                jh7110_obj->convolutionCore->ifm_sub_width = ((ifm_sub_width_p1 & 0x3f) << 6) + ifm_sub_width_p0;
                jh7110_obj->convolutionCore->ofm_sub_width_flag = ofm_sub_width_p0_flag;
                jh7110_obj->convolutionCore->ofm_sub_width = ofm_sub_width_p0 + ((ofm_sub_width_p1 & 0x3ff) << 10) + ((ofm_sub_width_p2 & 0x3ff) << 20);
                jh7110_obj->convolutionCore->cvnn_start = true;
                jh7110_obj->convolutionCore->Run();
                ofm_line_idx += jh7110_obj->convolutionCore->ofm_height;
            }
        }
    }

    //NOTE: not used by now, since conv path don't support ofm line stride
    //3. do tile division

    //TODO
    //4. divide ifm channel and do multi ofm line each run
    else {
        //std::cout << "case 4: not supported by now" << std::endl;
        std::exit(-1);
    }

    jh7110_obj->dram->read_mem(output, dram_output_addr, ofm_size);
    return;
}

void activation_op(void *input, void *output, int scale,
                   int ifm_width, int ifm_height, int ifm_num,
                   int activation_type, int lrelu_lmbda, int out_shift)
{
    CHECK(ifm_width <= 0xfff);
    CHECK(ifm_height <= 0xfff);
    CHECK(ifm_num <= 0x1fff);
    CHECK(out_shift <= 0x1f);

    u32 op_type = 0x1;

    int ofm_num = ifm_num;
    u32 ifm_size_per_ch = ifm_width * ifm_height;
    //u32 ifm_offset = ifm_size_per_ch;
    u32 ifm_size = ifm_size_per_ch * ifm_num;

    u32 ofm_width = ifm_width;
    u32 ofm_height = ifm_height;
    u32 ofm_size_per_ch = ofm_width * ofm_height;
    //u32 ofm_offset = ofm_size_per_ch;
    u32 ofm_size = ofm_size_per_ch * ofm_num;

    u32 knl_size_per_ch = 1;
    //u32 knl_offset = knl_size_per_ch;
    u32 knl_size = knl_size_per_ch * ofm_num;

    auto jh7110_obj = std::make_shared<JH7110>();

    u64 dram_input_addr = 0x0;
    jh7110_obj->dram->write_mem(dram_input_addr, input, ifm_size);

    u64 dram_knl_addr = ALIGN_32(dram_input_addr + ifm_size);
    u8* weight_array = new u8[knl_size];
    memset(weight_array, 1, knl_size);
    jh7110_obj->dram->write_mem(dram_knl_addr, weight_array, knl_size);
    delete [] weight_array;

    u64 dram_bias_scale_addr = ALIGN_32(dram_knl_addr + knl_size);
    u32 bias = 0;
    for (int i = 0; i < ofm_num; i++) {
        jh7110_obj->dram->write_mem(dram_bias_scale_addr + 2 * i * sizeof(u32), &bias, sizeof(u32));
        jh7110_obj->dram->write_mem(dram_bias_scale_addr + (2 * i + 1) * sizeof(u32), &scale, sizeof(u32));
    }

    u64 dram_output_addr = ALIGN_32(dram_bias_scale_addr + ofm_num * 2 * sizeof(u32));

    //1. do multi ofm channel each run
    if (ALIGN_32(ifm_size_per_ch) + (ALIGN_32(knl_size_per_ch) + ALIGN_32(2 * sizeof(u32))) <= 2 * 1024 * 1024) {
        //std::cout << "case 1: multi ofm channel each run" << std::endl;
        int ofm_num_each_run = 1;
        for (int i = 1; i <= ofm_num; i++) {
            if (ALIGN_32(ifm_size_per_ch * i) + ALIGN_32(knl_size_per_ch * i) + ALIGN_32(2 * sizeof(u32) * i) > 2 * 1024 * 1024) {
                ofm_num_each_run = i - 1;
                break;
            }
            else if (i >= ofm_num) {
                ofm_num_each_run = ofm_num;
                break;
            }
        }

        int ofm_num_cur_run;
        int ofm_num_remain;
        for (int i = 0; i < ofm_num; i += ofm_num_cur_run) {
            ofm_num_remain = ofm_num - i;
            ofm_num_cur_run = (ofm_num_remain > ofm_num_each_run) ? ofm_num_each_run : ofm_num_remain;

            //ifm dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = ((dram_input_addr + i * ifm_size_per_ch)& 0xf);
            jh7110_obj->dma->vgrRaddr_h[0] = ((dram_input_addr + i * ifm_size_per_ch) & (((u64)0xffffffff) << 4)) >> 4;
            jh7110_obj->dma->dramAddrStep[0] = ifm_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->burstDataLen[0] = ifm_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->burstDataTimes[0] = 1;
            jh7110_obj->dma->writeDataLen[0] = ifm_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = ifm_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;
            jh7110_obj->dma->vgrEn[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            //knl dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = ((dram_knl_addr + knl_size_per_ch * i) & 0xf);
            jh7110_obj->dma->vgrRaddr_h[0] = ((dram_knl_addr + knl_size_per_ch * i) & (((u64)0xffffffff) << 4)) >> 4;
            jh7110_obj->dma->dramAddrStep[0] = knl_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->burstDataLen[0] = knl_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->burstDataTimes[0] = 1;
            jh7110_obj->dma->writeDataLen[0] = knl_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size_per_ch * ofm_num_cur_run)) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size_per_ch * ofm_num_cur_run)) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = knl_size_per_ch * ofm_num_cur_run;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;

            //bias, scale dma
            jh7110_obj->dma->vgrEn[1] = 1;
            jh7110_obj->dma->vgrRaddr_l[1] = ((dram_bias_scale_addr + sizeof(u32) * 2 * i) & 0xf);
            jh7110_obj->dma->vgrRaddr_h[1] = ((dram_bias_scale_addr + sizeof(u32) * 2 * i) & (((u64)0xffffffff) << 4)) >> 4;
            jh7110_obj->dma->dramAddrStep[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->burstDataLen[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->burstDataTimes[1] = 1;
            jh7110_obj->dma->writeDataLen[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->ramWaddr[1] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) + ALIGN_32(knl_size_per_ch * ofm_num_cur_run)) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[1] = ((jh7110_obj->convCoreStartAddr + ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) + ALIGN_32(knl_size_per_ch * ofm_num_cur_run)) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[1] = sizeof(u32) * 2 * ofm_num_cur_run;
            jh7110_obj->dma->mode[1] = 0x0;
            jh7110_obj->dma->scale[1] = 0;
            jh7110_obj->dma->padZero[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            u32 ifm_sub_width_p0;
            u32 ifm_sub_width_p1;
            u32 ifm_line_stride_p0;
            u32 ifm_line_stride_p1;
            u32 ofm_sub_width_p0;
            u32 ofm_sub_width_p1;
            u32 ofm_sub_width_p2;
            u32 ofm_sub_width_p0_flag;
            ifm_param_gen(ifm_width, ofm_width, 1, 1, 0, 0,
                          ifm_sub_width_p0, ifm_sub_width_p1, ifm_line_stride_p0, ifm_line_stride_p1,
                          ofm_sub_width_p0, ofm_sub_width_p1, ofm_sub_width_p2, ofm_sub_width_p0_flag);

            jh7110_obj->convolutionCore->irq_status = false;
            jh7110_obj->convolutionCore->kernel_stride = (1 << 16) + 1;
            jh7110_obj->convolutionCore->ifm_line_stride = ((ifm_line_stride_p1 & 0x3f) << 6) + (ifm_line_stride_p0 & 0x3f);
            jh7110_obj->convolutionCore->ifm_width = (ifm_width & 0xfff) + ((jh7110_obj->convolutionCore->ifm_line_stride & 0xfff) << 16);
            jh7110_obj->convolutionCore->ifm_height = ifm_height;
            jh7110_obj->convolutionCore->ofm_width = ofm_width;
            jh7110_obj->convolutionCore->ofm_height = ofm_height;
            jh7110_obj->convolutionCore->ifm_num = ofm_num_cur_run;
            jh7110_obj->convolutionCore->ofm_num = ofm_num_cur_run;
            jh7110_obj->convolutionCore->padding_idc = 0;
            jh7110_obj->convolutionCore->padding_word = 0;
            jh7110_obj->convolutionCore->padding_size = 0;
            jh7110_obj->convolutionCore->op_type = op_type;
            if(activation_type == 0 || activation_type == 1)
            {
                jh7110_obj->convolutionCore->conv_detail = activation_type;
            }
            else if(activation_type == 2)
            {
                jh7110_obj->convolutionCore->conv_detail = 0x4 | (lrelu_lmbda << 8);
            }
            jh7110_obj->convolutionCore->conv_detail |= 0x2;
            jh7110_obj->convolutionCore->ifm_start_L = ((0x0 >>5) & 0xffffffff);
            jh7110_obj->convolutionCore->ifm_start_m = (((0x0 >>5) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->ifm_offset = (ifm_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
            jh7110_obj->convolutionCore->knl_start_L = ((ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) >> 5) & 0xffffffff);
            jh7110_obj->convolutionCore->knl_start_m = (((ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) >> 5) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->knl_offset = (knl_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
            jh7110_obj->convolutionCore->knl_size = knl_size_per_ch;
            jh7110_obj->convolutionCore->ofm_bias_start_L = (((ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) + ALIGN_32(knl_size_per_ch * ofm_num_cur_run)) >> 5) & 0xffffffff);
            jh7110_obj->convolutionCore->ofm_bias_start_m = ((((((ALIGN_32(ifm_size_per_ch * ofm_num_cur_run) + ALIGN_32(knl_size_per_ch * ofm_num_cur_run)) >> 5) & ((u64)0xf << 32)) >> 32) & 0x0000000f) + ((0x0 & 0x0000001f) << 16));
            jh7110_obj->convolutionCore->ofm_start_L = ((dram_output_addr + i * ofm_width * ofm_height) & 0xffffffff);
            jh7110_obj->convolutionCore->ofm_start_m = (((dram_output_addr + i * ofm_width * ofm_height) & ((u64)0xf << 32)) >> 32);
            jh7110_obj->convolutionCore->ofm_offset = ofm_size_per_ch;
            jh7110_obj->convolutionCore->out_shift = out_shift;
            //TODO: lut_bypass
            jh7110_obj->convolutionCore->lut_bypass = 1;
            jh7110_obj->convolutionCore->ifm_sub_width = ((ifm_sub_width_p1 & 0x3f) << 6) + ifm_sub_width_p0;
            jh7110_obj->convolutionCore->ofm_sub_width_flag = ofm_sub_width_p0_flag;
            jh7110_obj->convolutionCore->ofm_sub_width = ofm_sub_width_p0 + ((ofm_sub_width_p1 & 0x3ff) << 10) + ((ofm_sub_width_p2 & 0x3ff) << 20);
            jh7110_obj->convolutionCore->cvnn_start = true;
            jh7110_obj->convolutionCore->Run();
        }
    }
    //2. do multi ofm line each run
    else if (ALIGN_32(ifm_width) + (ALIGN_32(knl_size_per_ch) + ALIGN_32(2 * sizeof(u32))) <= 2 * 1024 * 1024) {
        //std::cout << "case 2: multi ofm line each run" << std::endl;
        int line_num_each_run = 1;
        for (int i = 1; i <= ifm_height; i++) {
            if (ALIGN_32(ifm_width * i) + ALIGN_32(knl_size_per_ch) + ALIGN_32(2 * sizeof(u32)) > 2 * 1024 * 1024) {
                line_num_each_run = i - 1;
                break;
            }
            else if (i >= ifm_height) {
                line_num_each_run = ifm_height;
                break;
            }
        }
        for (int i = 0; i < ofm_num; i++) {
            int line_num_cur_run;
            int line_num_reload;
            int line_idx = 0;
            int ofm_line_idx = 0;
            for (line_idx = 0; line_idx < ifm_height; line_idx += (line_num_cur_run - line_num_reload)) {
                line_num_cur_run = ((line_idx + line_num_each_run) > ifm_height) ? (ifm_height - line_idx): line_num_each_run;
                line_num_reload = 0;
                if (line_num_cur_run < 1) {
                    continue;
                }

                //ifm lines dma
                jh7110_obj->dma->vgrEn[0] = 1;
                jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + i * ifm_size_per_ch + line_idx * ifm_width) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[0] = ((dram_input_addr + i * ifm_size_per_ch + line_idx * ifm_width) & (((u64)0xffffffff) << 4)) >> 4;
                jh7110_obj->dma->dramAddrStep[0] = ifm_width * line_num_cur_run;
                jh7110_obj->dma->burstDataLen[0] = ifm_width * line_num_cur_run;
                jh7110_obj->dma->burstDataTimes[0] = 1;
                jh7110_obj->dma->writeDataLen[0] = ifm_width * line_num_cur_run;
                jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[0] = ifm_width * line_num_cur_run;
                jh7110_obj->dma->mode[0] = 0x0;
                jh7110_obj->dma->scale[0] = 0;
                jh7110_obj->dma->padZero[0] = 0;
                jh7110_obj->dma->vgrEn[1] = 0;
                jh7110_obj->dma->convDmaEn = 1;
                jh7110_obj->dma->Run();

                int sram_knl_addr = ALIGN_32(ifm_width * line_num_cur_run);
                int sram_bias_scale_addr = ALIGN_32(ifm_width * line_num_cur_run) + ALIGN_32(knl_size_per_ch);
                //knl dma
                jh7110_obj->dma->vgrEn[0] = 1;
                jh7110_obj->dma->vgrRaddr_l[0] = (dram_knl_addr + i * knl_size_per_ch) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[0] = ((dram_knl_addr + i * knl_size_per_ch) & (((u64)0xffffffff) << 4)) >> 4;
                jh7110_obj->dma->dramAddrStep[0] = knl_size_per_ch;
                jh7110_obj->dma->burstDataLen[0] = knl_size_per_ch;
                jh7110_obj->dma->burstDataTimes[0] = 1;
                jh7110_obj->dma->writeDataLen[0] = knl_size_per_ch;
                jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + sram_knl_addr) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + sram_knl_addr) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[0] = knl_size_per_ch;
                jh7110_obj->dma->mode[0] = 0x0;
                jh7110_obj->dma->scale[0] = 0;
                jh7110_obj->dma->padZero[0] = 0;
                //bias scale dma
                jh7110_obj->dma->vgrEn[1] = 1;
                jh7110_obj->dma->vgrRaddr_l[1] = (dram_bias_scale_addr + i * 2 * sizeof(u32)) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[1] = ((dram_bias_scale_addr + i * 2 * sizeof(u32)) & (((u64)0xffffffff) << 4)) >> 4;
                jh7110_obj->dma->dramAddrStep[1] = 2 * sizeof(u32);
                jh7110_obj->dma->burstDataLen[1] = 2 * sizeof(u32);
                jh7110_obj->dma->burstDataTimes[1] = 1;
                jh7110_obj->dma->writeDataLen[1] = 2 * sizeof(u32);
                jh7110_obj->dma->ramWaddr[1] = ((jh7110_obj->convCoreStartAddr + sram_bias_scale_addr) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[1] = ((jh7110_obj->convCoreStartAddr + sram_bias_scale_addr) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[1] = 2 * sizeof(u32);
                jh7110_obj->dma->mode[1] = 0x0;
                jh7110_obj->dma->scale[1] = 0;
                jh7110_obj->dma->padZero[1] = 0;
                jh7110_obj->dma->convDmaEn = 1;
                jh7110_obj->dma->Run();

                //do conv
                u32 ifm_sub_width_p0;
                u32 ifm_sub_width_p1;
                u32 ifm_line_stride_p0;
                u32 ifm_line_stride_p1;
                u32 ofm_sub_width_p0;
                u32 ofm_sub_width_p1;
                u32 ofm_sub_width_p2;
                u32 ofm_sub_width_p0_flag;
                ifm_param_gen(ifm_width, ofm_width, 1, 1, 0, 0,
                              ifm_sub_width_p0, ifm_sub_width_p1, ifm_line_stride_p0, ifm_line_stride_p1,
                              ofm_sub_width_p0, ofm_sub_width_p1, ofm_sub_width_p2, ofm_sub_width_p0_flag);
                jh7110_obj->convolutionCore->irq_status = false;
                jh7110_obj->convolutionCore->kernel_stride = (1 << 16) + 1;
                //TODO: ifm_line_stride
                jh7110_obj->convolutionCore->ifm_line_stride = ((ifm_line_stride_p1 & 0x3f) << 6) + (ifm_line_stride_p0 & 0x3f);
                jh7110_obj->convolutionCore->ifm_width = (ifm_width & 0xfff) + ((jh7110_obj->convolutionCore->ifm_line_stride & 0xfff) << 16);
                jh7110_obj->convolutionCore->ifm_height = line_num_cur_run;
                jh7110_obj->convolutionCore->ofm_width = ofm_width;
                jh7110_obj->convolutionCore->ofm_height = line_num_cur_run;
                jh7110_obj->convolutionCore->ifm_num = 1;
                jh7110_obj->convolutionCore->ofm_num = 1;

                jh7110_obj->convolutionCore->padding_idc = 0;
                jh7110_obj->convolutionCore->padding_word = 0;
                jh7110_obj->convolutionCore->padding_size = 0;
                jh7110_obj->convolutionCore->op_type = op_type;
                if(activation_type == 0 || activation_type == 1)
                {
                    jh7110_obj->convolutionCore->conv_detail = activation_type;
                }
                else if(activation_type == 2)
                {
                    jh7110_obj->convolutionCore->conv_detail = 0x4 | (lrelu_lmbda << 8);
                }
                jh7110_obj->convolutionCore->conv_detail |= 0x2;
                jh7110_obj->convolutionCore->ifm_start_L = ((0x0 >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->ifm_start_m = (((0x0 >> 5) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->ifm_offset = ((ifm_width * line_num_cur_run) & 0x00ffffff) + ((0x0 & 0x1f) << 24);
                jh7110_obj->convolutionCore->knl_start_L = ((sram_knl_addr >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->knl_start_m = (((sram_knl_addr >> 5) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->knl_offset = (knl_size_per_ch & 0x00ffffff) + ((0x0 & 0x1f) << 24);
                jh7110_obj->convolutionCore->knl_size = knl_size_per_ch;
                jh7110_obj->convolutionCore->ofm_bias_start_L = ((sram_bias_scale_addr >> 5) & 0xffffffff);
                jh7110_obj->convolutionCore->ofm_bias_start_m = (((((sram_bias_scale_addr >> 5) & ((u64)0xf << 32)) >> 32) & 0x0000000f) + ((0x0 & 0x0000001f) << 16));
                jh7110_obj->convolutionCore->ofm_start_L = ((dram_output_addr + i * ofm_width * ofm_height + ofm_line_idx * ofm_width) & 0xffffffff);
                jh7110_obj->convolutionCore->ofm_start_m = (((dram_output_addr + i * ofm_width * ofm_height + ofm_line_idx * ofm_width) & ((u64)0xf << 32)) >> 32);
                jh7110_obj->convolutionCore->ofm_offset = ofm_size_per_ch;
                jh7110_obj->convolutionCore->out_shift = out_shift;
                //TODO: lut_bypass
                jh7110_obj->convolutionCore->lut_bypass = 1;
                jh7110_obj->convolutionCore->ifm_sub_width = ((ifm_sub_width_p1 & 0x3f) << 6) + ifm_sub_width_p0;
                jh7110_obj->convolutionCore->ofm_sub_width_flag = ofm_sub_width_p0_flag;
                jh7110_obj->convolutionCore->ofm_sub_width = ofm_sub_width_p0 + ((ofm_sub_width_p1 & 0x3ff) << 10) + ((ofm_sub_width_p2 & 0x3ff) << 20);
                jh7110_obj->convolutionCore->cvnn_start = true;
                jh7110_obj->convolutionCore->Run();
                ofm_line_idx += jh7110_obj->convolutionCore->ofm_height;
            }
        }
    }

    //NOTE: not used by now, since conv path don't support ofm line stride
    //3. do tile division

    //TODO
    //4. divide ifm channel and do multi ofm line each run
    else {
        //std::cout << "case 4: not supported by now" << std::endl;
        std::exit(-1);
    }

    jh7110_obj->dram->read_mem(output, dram_output_addr, ofm_size);
    return;
}

void fc_op(void *input, void *weight, void *bias, void *scale, void *output,
           int fc_input_size, int fc_output_size, int activation_type,
           int lrelu_lmbda, int out_shift)
{
    CHECK(fc_input_size <= 0xffff);
    CHECK(fc_output_size <= 0x1fff);

    auto jh7110_obj = std::make_shared<JH7110>();

    u64 dram_input_addr = 0x0;
    jh7110_obj->dram->write_mem(dram_input_addr, input, fc_input_size);

    u64 dram_knl_addr = ALIGN_32(dram_input_addr + fc_input_size);
    jh7110_obj->dram->write_mem(dram_knl_addr, weight, fc_input_size * fc_output_size);

    u64 dram_bias_scale_addr = ALIGN_32(dram_knl_addr + fc_input_size * fc_output_size);
    for (int i = 0; i < fc_output_size; i++) {
        jh7110_obj->dram->write_mem(dram_bias_scale_addr + 2 * i * sizeof(u32), ((u32 *) bias) + i, sizeof(u32));
        jh7110_obj->dram->write_mem(dram_bias_scale_addr + (2 * i + 1) * sizeof(u32), ((u32 *)scale) + i, sizeof(u32));
    }

    s8 zero_zone[48];
    memset(&zero_zone[0], 0, sizeof(zero_zone));

    u64 dram_zero_zone_addr = ALIGN_32(dram_bias_scale_addr + fc_output_size * 2 * sizeof(u32));
    jh7110_obj->dram->write_mem(dram_zero_zone_addr, zero_zone, sizeof(zero_zone));

    u64 dram_output_addr = ALIGN_32(dram_zero_zone_addr + sizeof(zero_zone));

    u32 op_type = 1 << 6;

    int output_num_each_run = 1;
    for (int i = 1; i <= fc_output_size; i++) {
        if (ALIGN_32(ALIGN_48(fc_input_size)) + ALIGN_32(ALIGN_48(fc_input_size) * i) + ALIGN_32(2 * sizeof(u32) * i) > 2 * 1024 * 1024) {
            output_num_each_run = i - 1;
            break;
        }
        else if (i >= fc_output_size) {
            output_num_each_run = fc_output_size;
            break;
        }
    }

    //ifm dma
    jh7110_obj->dma->vgrEn[0] = 1;
    jh7110_obj->dma->vgrRaddr_l[0] = dram_input_addr & 0xf;
    jh7110_obj->dma->vgrRaddr_h[0] = (dram_input_addr & (((u64)0xffffffff) << 4)) >> 4;
    jh7110_obj->dma->dramAddrStep[0] = fc_input_size;
    jh7110_obj->dma->burstDataLen[0] = fc_input_size;
    jh7110_obj->dma->burstDataTimes[0] = 1;
    jh7110_obj->dma->writeDataLen[0] = fc_input_size;
    jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
    jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + 0x0) & 0x1f);
    jh7110_obj->dma->ramWaddrStep[0] = fc_input_size;
    jh7110_obj->dma->mode[0] = 0x0;
    jh7110_obj->dma->scale[0] = 0;
    jh7110_obj->dma->padZero[0] = 0;
    jh7110_obj->dma->vgrEn[1] = 0;
    jh7110_obj->dma->convDmaEn = 1;
    jh7110_obj->dma->Run();

    //pad zero to ifm end
    if ((fc_input_size % 48) != 0) {
        jh7110_obj->dma->vgrEn[0] = 1;
        jh7110_obj->dma->vgrRaddr_l[0] = dram_zero_zone_addr & 0xf;
        jh7110_obj->dma->vgrRaddr_h[0] = (dram_zero_zone_addr & (((u64)0xffffffff) << 4)) >> 4;
        jh7110_obj->dma->dramAddrStep[0] = 48 - (fc_input_size % 48);
        jh7110_obj->dma->burstDataLen[0] = 48 - (fc_input_size % 48);
        jh7110_obj->dma->burstDataTimes[0] = 1;
        jh7110_obj->dma->writeDataLen[0] = 48 - (fc_input_size % 48);
        jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + fc_input_size) & ((0xffffffff) << 5)) >> 5;
        jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + fc_input_size) & 0x1f);
        jh7110_obj->dma->ramWaddrStep[0] = 48 - (fc_input_size % 48);
        jh7110_obj->dma->mode[0] = 0x0;
        jh7110_obj->dma->scale[0] = 0;
        jh7110_obj->dma->padZero[0] = 0;
        jh7110_obj->dma->vgrEn[1] = 0;
        jh7110_obj->dma->convDmaEn = 1;
        jh7110_obj->dma->Run();
    }


    int output_num_cur_run;
    int output_num_remain;
    for (int i = 0; i < fc_output_size; i += output_num_cur_run) {
        output_num_remain = fc_output_size - i;
        output_num_cur_run = (output_num_remain > output_num_each_run) ? output_num_each_run : output_num_remain;

        int sram_knl_addr = ALIGN_32(ALIGN_48(fc_input_size));
        int sram_bias_scale_addr = ALIGN_32(sram_knl_addr + ALIGN_48(fc_input_size) * output_num_cur_run);

        //knl dma
        jh7110_obj->dma->vgrEn[0] = 1;
        jh7110_obj->dma->vgrRaddr_l[0] = (dram_knl_addr + i * fc_input_size) & 0xf;
        jh7110_obj->dma->vgrRaddr_h[0] = ((dram_knl_addr + i * fc_input_size) & (((u64)0xffffffff) << 4)) >> 4;
        jh7110_obj->dma->dramAddrStep[0] = fc_input_size;
        jh7110_obj->dma->burstDataLen[0] = fc_input_size;
        jh7110_obj->dma->burstDataTimes[0] = output_num_cur_run;
        jh7110_obj->dma->writeDataLen[0] = fc_input_size;
        jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + sram_knl_addr) & ((0xffffffff) << 5)) >> 5;
        jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + sram_knl_addr) & 0x1f);
        jh7110_obj->dma->ramWaddrStep[0] = ALIGN_48(fc_input_size);
        jh7110_obj->dma->mode[0] = 0x0;
        jh7110_obj->dma->scale[0] = 0;
        jh7110_obj->dma->padZero[0] = 0;
        jh7110_obj->dma->vgrEn[1] = 0;
        jh7110_obj->dma->convDmaEn = 1;
        jh7110_obj->dma->Run();

        //bias/scale dma
        jh7110_obj->dma->vgrEn[0] = 1;
        jh7110_obj->dma->vgrRaddr_l[0] = (dram_bias_scale_addr + i * 2 * sizeof(u32)) & 0xf;
        jh7110_obj->dma->vgrRaddr_h[0] = ((dram_bias_scale_addr + i * 2 * sizeof(u32)) & (((u64)0xffffffff) << 4)) >> 4;
        jh7110_obj->dma->dramAddrStep[0] = output_num_cur_run * 2 * sizeof(u32);
        jh7110_obj->dma->burstDataLen[0] = output_num_cur_run * 2 * sizeof(u32);
        jh7110_obj->dma->burstDataTimes[0] = 1;
        jh7110_obj->dma->writeDataLen[0] = output_num_cur_run * 2 * sizeof(u32);
        jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->convCoreStartAddr + sram_bias_scale_addr) & ((0xffffffff) << 5)) >> 5;
        jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->convCoreStartAddr + sram_bias_scale_addr) & 0x1f);
        jh7110_obj->dma->ramWaddrStep[0] = output_num_cur_run * 2 * sizeof(u32);
        jh7110_obj->dma->mode[0] = 0x0;
        jh7110_obj->dma->scale[0] = 0;
        jh7110_obj->dma->padZero[0] = 0;
        jh7110_obj->dma->vgrEn[1] = 0;
        jh7110_obj->dma->convDmaEn = 1;
        jh7110_obj->dma->Run();

        //do fc
        u32 ifm_sub_width = 12;
        u32 ofm_sub_width = 1;
        u32 ofm_sub_width_flag = 0;

        jh7110_obj->convolutionCore->irq_status = false;
        jh7110_obj->convolutionCore->kernel_stride = (12 << 16) + 4;
        jh7110_obj->convolutionCore->ifm_line_stride = 0x0;
        jh7110_obj->convolutionCore->ifm_width = (12 & 0xfff) + ((jh7110_obj->convolutionCore->ifm_line_stride & 0xfff) << 16);
        jh7110_obj->convolutionCore->ifm_height = 4;
        jh7110_obj->convolutionCore->ofm_width = 1;
        jh7110_obj->convolutionCore->ofm_height = 1;
        jh7110_obj->convolutionCore->ifm_num = ALIGN_48(fc_input_size) / 48;
        jh7110_obj->convolutionCore->ofm_num = output_num_cur_run;
        jh7110_obj->convolutionCore->padding_idc = 0;
        jh7110_obj->convolutionCore->padding_word = 0;
        jh7110_obj->convolutionCore->padding_size = 0;
        jh7110_obj->convolutionCore->op_type = op_type;
        if(activation_type == 0 || activation_type ==1)
        {
            jh7110_obj->convolutionCore->fc_detail = activation_type;
        }
        else if(activation_type == 2)
        {
            jh7110_obj->convolutionCore->fc_detail = 0x2 | (lrelu_lmbda << 8);
        }
        jh7110_obj->convolutionCore->fc_input_size = ALIGN_48(fc_input_size);
        jh7110_obj->convolutionCore->fc_output_size = output_num_cur_run;
        jh7110_obj->convolutionCore->ifm_start_L = ((0x0 >> 5) & 0xffffffff);
        jh7110_obj->convolutionCore->ifm_start_m = (((0x0 >> 5) & ((u64)0xf << 32)) >> 32);
        jh7110_obj->convolutionCore->ifm_offset = (48 & 0x00ffffff) + ((0x0 & 0x1f) << 24);
        jh7110_obj->convolutionCore->knl_start_L = ((sram_knl_addr >> 5) & 0xffffffff);
        jh7110_obj->convolutionCore->knl_start_m = (((sram_knl_addr >> 5) & ((u64)0xf << 32)) >> 32);
        jh7110_obj->convolutionCore->knl_offset = (48 & 0x00ffffff) + ((0x0 & 0x1f) << 24);
        jh7110_obj->convolutionCore->knl_size = 48;
        jh7110_obj->convolutionCore->ofm_bias_start_L = ((sram_bias_scale_addr >> 5) & 0xffffffff);
        jh7110_obj->convolutionCore->ofm_bias_start_m = ((((sram_bias_scale_addr >> 5) & ((u64)0xf << 32)) >> 32) & 0x0000000f) + ((0x0 & 0x0000001f) << 16);
        jh7110_obj->convolutionCore->ofm_start_L = ((dram_output_addr + i) & 0xffffffff);
        jh7110_obj->convolutionCore->ofm_start_m = (((dram_output_addr + i) & ((u64)0xf << 32)) >> 32);
        jh7110_obj->convolutionCore->ofm_offset = 1;
        jh7110_obj->convolutionCore->out_shift = out_shift;
        //TODO: lut_bypass
        jh7110_obj->convolutionCore->lut_bypass = 1;
        jh7110_obj->convolutionCore->ifm_sub_width = ifm_sub_width;
        jh7110_obj->convolutionCore->ofm_sub_width_flag = ofm_sub_width_flag;
        jh7110_obj->convolutionCore->ofm_sub_width = ofm_sub_width;
        jh7110_obj->convolutionCore->cvnn_start = true;
        jh7110_obj->convolutionCore->Run();
    }

    jh7110_obj->dram->read_mem(output, dram_output_addr, fc_output_size);
    return;
}

void avepool_op(void *input, void *output,
                int ifm_width, int ifm_height, int ifm_num,
                int knl_h, int knl_w, int stride_h, int stride_w,
                int pad_t, int pad_b, int pad_l, int pad_r, int pad_w,
                int re_mul_num, int out_shift)
{
    u32 ofm_num = ifm_num;
    u32 ifm_offset = ifm_width * ifm_height;
    u32 ifm_size = ifm_offset * ifm_num;
    u32 ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
    u32 ofm_height = (ifm_height + pad_t + pad_b - knl_h) / stride_h + 1;
    u32 ofm_offset = ofm_width * ofm_height;
    u32 ofm_size = ofm_offset * ofm_num;
    auto dram_obj = std::make_shared<Dram>(1024);
    auto vector_obj = std::make_shared<VectorPath>(dram_obj);
    u32 ofm_addr = 1 << 20; /// ofm_addr to be done 
    dram_obj->write_mem(0, input, ifm_size);
    vector_obj->ifm_start_addr_h[0] = 0;
    vector_obj->ifm_start_addr_l[0] = 0;
    vector_obj->ofm_start_addr_h = 0;
    vector_obj->ofm_start_addr_l = ofm_addr;
    if(knl_w == 2 && knl_h == 2){
        vector_obj->pe_mode = 1 << 2;
    }
    else if(knl_w == 3 && knl_h ==3){
        vector_obj->pe_mode = 1 << 3;
        //vector_obj->re_mul_num = (u32)0x1c71c71c;
        //vector_obj->out_shift = 0;
    }
    else if(knl_w == 4 && knl_h ==4){
        vector_obj->pe_mode = 1 << 4;
    }
    else{
        CHECK_MSG(false, "average pooling size error");
    }
    vector_obj->re_mul_num = re_mul_num;
    vector_obj->out_shift = out_shift;
    vector_obj->pooling_stride = stride_w + (stride_h << 4);
    u32 padding_idc = 0;
    u32 padding_size = 0;
    if(pad_l){
        padding_size |= (pad_l << 0);
        padding_idc |= 1;
    }
    if(pad_r){
        padding_size |= (pad_r << 4);
        padding_idc |= 2;
    }
    if(pad_t){
        padding_size |= (pad_t << 8);
        padding_idc |= 4;
    }
    if(pad_b){
        padding_size |= (pad_b << 12);
        padding_idc |= 8;
    }
    vector_obj->padding_idc = padding_idc;
    vector_obj->padding_word = 0;
    vector_obj->padding_size = padding_size;

    vector_obj->ifm_width = ifm_width;
    vector_obj->ifm_height = ifm_height;
    vector_obj->ofm_width = ofm_width;
    vector_obj->ofm_height = ofm_height;
    vector_obj->ifm_num = ifm_num;
    vector_obj->ofm_num = ofm_num;
    vector_obj->ifm_offset[0] = ifm_offset;
    vector_obj->ofm_offset = ofm_offset;
    
    vector_obj->pe_done = false;
    vector_obj->pe_start = true;
    vector_obj->Run();

    dram_obj->read_mem(output, ofm_addr, ofm_size);
    return;
}

void avepool_op_v2(void *input, void *output,
                   int ifm_width, int ifm_height, int ifm_num,
                   int knl_h, int knl_w, int stride_h, int stride_w,
                   int pad_t, int pad_b, int pad_l, int pad_r, int pad_w,
                   int scale_num, int scale_shift)
{
    u32 vector_type = 0x0;
    if (knl_w == 2 && knl_h == 2){
        vector_type = 1 << 2;
    }
    else if(knl_w == 3 && knl_h ==3){
        vector_type = 1 << 3;
    }
    else if(knl_w == 4 && knl_h ==4){
        vector_type = 1 << 4;
    }
    else{
        CHECK_MSG(false, "average pooling size error");
    }

    u32 ifm_size_per_ch = ifm_width * ifm_height;
    //u32 ifm_offset = ifm_size_per_ch;
    u32 ifm_size = ifm_size_per_ch * ifm_num;

    u32 ofm_num = ifm_num;
    u32 ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
    u32 ofm_height = (ifm_height + pad_t + pad_b - knl_h) / stride_h + 1;
    u32 ofm_size_per_ch = ofm_width * ofm_height;
    //u32 ofm_offset = ofm_size_per_ch;
    u32 ofm_size = ofm_size_per_ch * ofm_num;

    auto jh7110_obj = std::make_shared<JH7110>();

    u64 dram_input_addr = 0x0;
    jh7110_obj->dram->write_mem(dram_input_addr, input, ifm_size);
    u64 dram_output_addr = (ifm_size + 16 - 1) / 16 * 16;

    u32 ifm_width_aligned = (ifm_width + 16 - 1) / 16 * 16;
    u32 ifm_size_per_ch_aligned = ifm_width_aligned * ifm_height;
    //1. do multi channel each run
    if (ifm_size_per_ch_aligned <= 8192) {

        u32 padding_size = 0;
        if(pad_l){
            padding_size |= ((pad_l & 0xf) << 0);
        }
        if(pad_r){
            padding_size |= ((pad_r & 0xf) << 4);
        }
        if(pad_t){
            padding_size |= ((pad_t & 0xf) << 8);
        }
        if(pad_b){
            padding_size |= ((pad_b & 0xf) << 12);
        }

        int ifm_num_each_run = 8192 / ifm_size_per_ch_aligned;
        int ifm_num_cur_run;
        int ifm_num_remain;
        for (int i = 0; i < ifm_num; i += ifm_num_cur_run) {
            ifm_num_remain = ifm_num - i;
            ifm_num_cur_run = (ifm_num_remain > ifm_num_each_run ? ifm_num_each_run : ifm_num_remain);

            //ifm dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + i * ifm_size_per_ch) & 0xf;
            jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_addr + i * ifm_size_per_ch) & (((u64)0xffffffff) << 4)) >> 4);
            jh7110_obj->dma->dramAddrStep[0] = ifm_width;
            jh7110_obj->dma->burstDataLen[0] = ifm_width;
            jh7110_obj->dma->burstDataTimes[0] = ifm_height * ifm_num_cur_run;
            jh7110_obj->dma->writeDataLen[0] = ifm_width;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = ifm_width_aligned;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;
            jh7110_obj->dma->vgrEn[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            for (int j = 0; j < ifm_num_cur_run; j++) {
                //vector core
                jh7110_obj->peCore->irq_status_reg = false;
                jh7110_obj->peCore->irq_mask_reg = false;
                jh7110_obj->peCore->vector_type = vector_type;
                jh7110_obj->peCore->ifm_start_addr = (((j * ifm_size_per_ch_aligned) / 16) & 0xffff);
                jh7110_obj->peCore->ifm_width = ifm_width;
                jh7110_obj->peCore->ifm_height = ifm_height;
                jh7110_obj->peCore->ifm_size = ifm_size_per_ch;
                jh7110_obj->peCore->ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
                jh7110_obj->peCore->ofm_height = (ifm_height + pad_t + pad_b - knl_h) / stride_h + 1;
                jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + (i + j) * jh7110_obj->peCore->ofm_width * jh7110_obj->peCore->ofm_height) & (((u64)0xf) << 32)) >> 32);
                jh7110_obj->peCore->ofm_start_l = (dram_output_addr + (i + j) * jh7110_obj->peCore->ofm_width * jh7110_obj->peCore->ofm_height) & 0xffffffff;
                jh7110_obj->peCore->ofm_addr_w_offset = ((jh7110_obj->peCore->ofm_width) & 0xfff);
                jh7110_obj->peCore->scale_shift = (scale_shift & 0x3f);
                jh7110_obj->peCore->scale_num = (scale_num & 0xffffffff);
                jh7110_obj->peCore->padding_word = 0;
                jh7110_obj->peCore->padding_size = padding_size;
                jh7110_obj->peCore->pooling_stride = ((stride_h & 0xf) << 4) + (stride_w & 0xf);
                jh7110_obj->peCore->reduce_mode = 0x0;
                jh7110_obj->peCore->ifm_rd_step_config = ((ifm_width_aligned / 16) & 0xffff) + (((((ifm_width_aligned / 16) & 0xffff) * stride_h) & 0xffff) << 16);
                jh7110_obj->peCore->vector_start = true;
                jh7110_obj->peCore->Run();
            }
        }
    }
    //2. do multi line each run
    else if (ifm_width_aligned <= 1024){
        int line_num_each_run = 8192 / ifm_width_aligned;
        int line_num_cur_run;
        int line_num_reload;
        int line_idx = 0;
        int ofm_line_idx = 0;
        for (line_idx = 0; line_idx < ifm_num * ifm_height; line_idx += (line_num_cur_run - line_num_reload)) {
            line_num_cur_run = (((line_idx % ifm_height) + line_num_each_run) > ifm_height) ? (ifm_height - (line_idx % ifm_height)): line_num_each_run;
            line_num_reload = (((line_idx + line_num_cur_run) % ifm_height) == 0) ? 0 : line_num_cur_run - (((line_num_cur_run + pad_t * ((line_idx % ifm_height) == 0) - knl_h) / stride_h + 1) * stride_h - pad_t * ((line_idx % ifm_height) == 0));

            if ((line_num_cur_run + pad_t * ((line_idx % ifm_height) == 0) + pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0)) < knl_h) {
                continue;
            }

            //dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + line_idx * ifm_width) & 0xf;
            jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_addr + line_idx * ifm_width) & (((u64)0xffffffff) << 4)) >> 4);
            jh7110_obj->dma->dramAddrStep[0] = ifm_width;
            jh7110_obj->dma->burstDataLen[0] = ifm_width;
            jh7110_obj->dma->burstDataTimes[0] = line_num_cur_run;
            jh7110_obj->dma->writeDataLen[0] = ifm_width;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = ifm_width_aligned;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;
            jh7110_obj->dma->vgrEn[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            //pooling
            jh7110_obj->peCore->irq_status_reg = false;
            jh7110_obj->peCore->irq_mask_reg = false;
            jh7110_obj->peCore->vector_type = vector_type;
            jh7110_obj->peCore->ifm_start_addr = (((0) / 16) & 0xffff);
            jh7110_obj->peCore->ifm_width = ifm_width;
            jh7110_obj->peCore->ifm_height = line_num_cur_run;
            jh7110_obj->peCore->ifm_size = ifm_width * line_num_cur_run;
            jh7110_obj->peCore->ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
            jh7110_obj->peCore->ofm_height = (line_num_cur_run +
                                      pad_t * ((line_idx % ifm_height) == 0) +
                                      pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0) -
                                      knl_h) / stride_h + 1;
            jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + ofm_line_idx * jh7110_obj->peCore->ofm_width) & (((u64)0xf) << 32)) >> 32);
            jh7110_obj->peCore->ofm_start_l = (dram_output_addr + ofm_line_idx * jh7110_obj->peCore->ofm_width) & 0xffffffff;
            jh7110_obj->peCore->ofm_addr_w_offset = ((jh7110_obj->peCore->ofm_width) & 0xfff);
            jh7110_obj->peCore->scale_shift = (scale_shift & 0x3f);
            jh7110_obj->peCore->scale_num = (scale_num & 0xffffffff);
            jh7110_obj->peCore->padding_word = 0;

            u32 padding_size = 0;
            if(pad_l){
                padding_size |= ((pad_l & 0xf) << 0);
            }
            if(pad_r){
                padding_size |= ((pad_r & 0xf) << 4);
            }
            if(pad_t){
                padding_size |= (((pad_t * ((line_idx % ifm_height) == 0)) & 0xf) << 8);
            }
            if(pad_b){
                padding_size |= (((pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0)) & 0xf) << 12);
            }

            jh7110_obj->peCore->padding_size = padding_size;
            jh7110_obj->peCore->pooling_stride = ((stride_h & 0xf) << 4) + (stride_w & 0xf);
            jh7110_obj->peCore->reduce_mode = 0x0;
            jh7110_obj->peCore->ifm_rd_step_config = ((ifm_width_aligned / 16) & 0xffff) + (((((ifm_width_aligned / 16) & 0xffff) * stride_h) & 0xffff) << 16);
            jh7110_obj->peCore->vector_start = true;
            jh7110_obj->peCore->Run();
            ofm_line_idx += jh7110_obj->peCore->ofm_height;
        }
    }
    //3. do tile division
    else {
        int col_num_each_run = 1024;
        int line_num_each_run = 8192 / 1024;
        int line_idx = 0;
        int line_num_cur_run;
        int line_num_reload;
        int col_idx = 0;
        int col_num_cur_run;
        int col_num_reload;
        int ofm_line_idx = 0;
        for (line_idx = 0; line_idx < ifm_num * ifm_height; line_idx += (line_num_cur_run - line_num_reload)) {
            line_num_cur_run = (((line_idx % ifm_height) + line_num_each_run) > ifm_height) ? (ifm_height - (line_idx % ifm_height)): line_num_each_run;
            line_num_reload = (((line_idx + line_num_cur_run) % ifm_height) == 0) ? 0 : line_num_cur_run - (((line_num_cur_run + pad_t * ((line_idx % ifm_height) == 0) - knl_h) / stride_h + 1) * stride_h - pad_t * ((line_idx % ifm_height) == 0));

            if ((line_num_cur_run + pad_t * ((line_idx % ifm_height) == 0) + pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0)) < knl_h) {
                continue;
            }

            int ofm_col_idx = 0;
            for (col_idx = 0; col_idx < ifm_width; col_idx += (col_num_cur_run - col_num_reload)) {
                col_num_cur_run = ((ifm_width - col_idx) > col_num_each_run) ? col_num_each_run : (ifm_width - col_idx);
                col_num_reload = ((col_idx + col_num_cur_run) == ifm_width) ? 0 : col_num_cur_run - (((col_num_cur_run + pad_l * (col_idx == 0) - knl_w) / stride_w + 1) * stride_w - pad_l * (col_idx == 0));

                if ((col_num_cur_run + pad_l * (col_idx == 0) + pad_r * ((col_idx + col_num_cur_run) == ifm_width)) < knl_w) {
                    continue;
                }

                //dma
                jh7110_obj->dma->vgrEn[0] = 1;
                jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + line_idx * ifm_width + col_idx) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_addr + line_idx * ifm_width + col_idx) & (((u64)0xffffffff) << 4)) >> 4);
                jh7110_obj->dma->dramAddrStep[0] = ifm_width;
                jh7110_obj->dma->burstDataLen[0] = col_num_cur_run;
                jh7110_obj->dma->burstDataTimes[0] = line_num_cur_run;
                jh7110_obj->dma->writeDataLen[0] = col_num_cur_run;
                jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[0] = (col_num_cur_run + 16 - 1) / 16 * 16;
                jh7110_obj->dma->mode[0] = 0x0;
                jh7110_obj->dma->scale[0] = 0;
                jh7110_obj->dma->padZero[0] = 0;
                jh7110_obj->dma->vgrEn[1] = 0;
                jh7110_obj->dma->convDmaEn = 1;
                jh7110_obj->dma->Run();

                //pooling
                jh7110_obj->peCore->irq_status_reg = false;
                jh7110_obj->peCore->irq_mask_reg = false;
                jh7110_obj->peCore->vector_type = vector_type;
                jh7110_obj->peCore->ifm_start_addr = (((0) / 16) & 0xffff);
                jh7110_obj->peCore->ifm_width = col_num_cur_run;
                jh7110_obj->peCore->ifm_height = line_num_cur_run;
                jh7110_obj->peCore->ifm_size = col_num_cur_run * line_num_cur_run;
                jh7110_obj->peCore->ofm_width = (col_num_cur_run +
                                         pad_l * (col_idx == 0) +
                                         pad_r * ((col_idx + col_num_cur_run) == ifm_width) -
                                         knl_w) / stride_w + 1;
                jh7110_obj->peCore->ofm_height = (line_num_cur_run +
                                          pad_t * ((line_idx % ifm_height) == 0) +
                                          pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0) -
                                          knl_h) / stride_h + 1;
                jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + ofm_line_idx * ofm_width + ofm_col_idx) & (((u64)0xf) << 32)) >> 32);
                jh7110_obj->peCore->ofm_start_l = (dram_output_addr + ofm_line_idx * ofm_width + ofm_col_idx) & 0xffffffff;

                jh7110_obj->peCore->ofm_addr_w_offset = (ofm_width & 0xfff);
                jh7110_obj->peCore->scale_shift = (scale_shift & 0x3f);
                jh7110_obj->peCore->scale_num = (scale_num & 0xffffffff);
                jh7110_obj->peCore->padding_word = 0;

                u32 padding_size = 0;
                if(pad_l){
                    padding_size |= (((pad_l * (col_idx == 0)) & 0xf) << 0);
                }
                if(pad_r){
                    padding_size |= (((pad_r * ((col_idx + col_num_cur_run) == ifm_width)) & 0xf) << 4);
                }
                if(pad_t){
                    padding_size |= (((pad_t * ((line_idx % ifm_height) == 0)) & 0xf) << 8);
                }
                if(pad_b){
                    padding_size |= (((pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0)) & 0xf) << 12);
                }

                jh7110_obj->peCore->padding_size = padding_size;
                jh7110_obj->peCore->pooling_stride = ((stride_h & 0xf) << 4) + (stride_w & 0xf);
                jh7110_obj->peCore->reduce_mode = 0x0;
                jh7110_obj->peCore->ifm_rd_step_config = (((col_num_cur_run + 16 - 1) / 16) & 0xffff) + ((((((col_num_cur_run + 16 - 1) / 16) & 0xffff) * stride_h) & 0xffff) << 16);
                jh7110_obj->peCore->vector_start = true;
                jh7110_obj->peCore->Run();
                ofm_col_idx += jh7110_obj->peCore->ofm_width;
            }
            ofm_line_idx += jh7110_obj->peCore->ofm_height;
        }
    }
    jh7110_obj->dram->read_mem(output, dram_output_addr, ofm_size);
    return;
}

void maxpool_op(void *input, void *output,
                int ifm_width, int ifm_height, int ifm_num,
                int knl_h, int knl_w, int stride_h, int stride_w,
                int pad_t, int pad_b, int pad_l, int pad_r, int pad_w)
{
    u32 ofm_num = ifm_num;
    u32 ifm_offset = ifm_width * ifm_height;
    u32 ifm_size = ifm_offset * ifm_num;
    u32 ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
    u32 ofm_height = (ifm_height + pad_t + pad_b - knl_h) / stride_h + 1;
    u32 ofm_offset = ofm_width * ofm_height;
    u32 ofm_size = ofm_offset * ofm_num;
    auto dram_obj = std::make_shared<Dram>(1024);
    auto vector_obj = std::make_shared<VectorPath>(dram_obj);
    u32 ofm_addr = 1 << 20; 
    dram_obj->write_mem(0, input, ifm_size);
    vector_obj->ifm_start_addr_h[0] = 0;
    vector_obj->ifm_start_addr_l[0] = 0;
    vector_obj->ofm_start_addr_h = 0;
    vector_obj->ofm_start_addr_l = ofm_addr;
    if(knl_w == 2 && knl_h == 2){
        vector_obj->pe_mode = 1 << 5;
    }
    else if(knl_w == 3 && knl_h ==3){
        vector_obj->pe_mode = 1 << 6;
    }
    else if(knl_w == 4 && knl_h ==4){
        vector_obj->pe_mode = 1 << 7;
    }
    else{
        CHECK_MSG(false, "average pooling size error");
    }
    vector_obj->pooling_stride = stride_w + (stride_h << 4);
    u32 padding_idc = 0;
    u32 padding_size = 0;
    if(pad_l){
        padding_size |= (pad_l << 0);
        padding_idc |= 1;
    }
    if(pad_r){
        padding_size |= (pad_r << 4);
        padding_idc |= 2;
    }
    if(pad_t){
        padding_size |= (pad_t << 8);
        padding_idc |= 4;
    }
    if(pad_b){
        padding_size |= (pad_b << 12);
        padding_idc |= 8;
    }
    vector_obj->padding_idc = padding_idc;
    vector_obj->padding_word = -127;
    vector_obj->padding_size = padding_size;

    vector_obj->ifm_width = ifm_width;
    vector_obj->ifm_height = ifm_height;
    vector_obj->ofm_width = ofm_width;
    vector_obj->ofm_height = ofm_height;
    vector_obj->ifm_num = ifm_num;
    vector_obj->ofm_num = ofm_num;
    vector_obj->ifm_offset[0] = ifm_offset;
    vector_obj->ofm_offset = ofm_offset;

    vector_obj->pe_done = false;
    vector_obj->pe_start = true;
    vector_obj->Run();

    dram_obj->read_mem(output, ofm_addr, ofm_size);
    return;

}

void maxpool_op_v2(void *input, void *output,
                   int ifm_width, int ifm_height, int ifm_num,
                   int knl_h, int knl_w, int stride_h, int stride_w,
                   int pad_t, int pad_b, int pad_l, int pad_r, int pad_w)
{
    u32 vector_type = 0x0;
    if(knl_w == 2 && knl_h == 2){
        vector_type = 1 << 5;
    }
    else if(knl_w == 3 && knl_h ==3){
        vector_type = 1 << 6;
    }
    else if(knl_w == 4 && knl_h ==4){
        vector_type = 1 << 7;
    }
    else{
        CHECK_MSG(false, "max pooling size error");
    }

    u32 ifm_size_per_ch = ifm_width * ifm_height;
    //u32 ifm_offset = ifm_size_per_ch;
    u32 ifm_size = ifm_size_per_ch * ifm_num;

    u32 ofm_num = ifm_num;
    u32 ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
    u32 ofm_height = (ifm_height + pad_t + pad_b - knl_h) / stride_h + 1;
    u32 ofm_size_per_ch = ofm_width * ofm_height;
    //u32 ofm_offset = ofm_size_per_ch;
    u32 ofm_size = ofm_size_per_ch * ofm_num;

    auto jh7110_obj = std::make_shared<JH7110>();

    u64 dram_input_addr = 0x0;
    jh7110_obj->dram->write_mem(dram_input_addr, input, ifm_size);
    u64 dram_output_addr = (ifm_size + 16 - 1) / 16 * 16;

    u32 ifm_width_aligned = (ifm_width + 16 - 1) / 16 * 16;
    u32 ifm_size_per_ch_aligned = ifm_width_aligned * ifm_height;
    //1. do multi channel each run
    if (ifm_size_per_ch_aligned <= 8192) {

        u32 padding_size = 0;
        if(pad_l){
            padding_size |= ((pad_l & 0xf) << 0);
        }
        if(pad_r){
            padding_size |= ((pad_r & 0xf) << 4);
        }
        if(pad_t){
            padding_size |= ((pad_t & 0xf) << 8);
        }
        if(pad_b){
            padding_size |= ((pad_b & 0xf) << 12);
        }

        int ifm_num_each_run = 8192 / ifm_size_per_ch_aligned;
        int ifm_num_cur_run;
        int ifm_num_remain;
        for (int i = 0; i < ifm_num; i += ifm_num_cur_run) {
            ifm_num_remain = ifm_num - i;
            ifm_num_cur_run = (ifm_num_remain > ifm_num_each_run ? ifm_num_each_run : ifm_num_remain);

            //ifm dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + i * ifm_size_per_ch) & 0xf;
            jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_addr + i * ifm_size_per_ch) & (((u64)0xffffffff) << 4)) >> 4);
            jh7110_obj->dma->dramAddrStep[0] = ifm_width;
            jh7110_obj->dma->burstDataLen[0] = ifm_width;
            jh7110_obj->dma->burstDataTimes[0] = ifm_height * ifm_num_cur_run;
            jh7110_obj->dma->writeDataLen[0] = ifm_width;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = ifm_width_aligned;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;
            jh7110_obj->dma->vgrEn[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            for (int j = 0; j < ifm_num_cur_run; j++) {
                //vector core
                jh7110_obj->peCore->irq_status_reg = false;
                jh7110_obj->peCore->irq_mask_reg = false;
                jh7110_obj->peCore->vector_type = vector_type;
                jh7110_obj->peCore->ifm_start_addr = (((j * ifm_size_per_ch_aligned) / 16) & 0xffff);
                jh7110_obj->peCore->ifm_width = ifm_width;
                jh7110_obj->peCore->ifm_height = ifm_height;
                jh7110_obj->peCore->ifm_size = ifm_size_per_ch;
                jh7110_obj->peCore->ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
                jh7110_obj->peCore->ofm_height = (ifm_height + pad_t + pad_b - knl_h) / stride_h + 1;
                jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + (i + j) * jh7110_obj->peCore->ofm_width * jh7110_obj->peCore->ofm_height) & (((u64)0xf) << 32)) >> 32);
                jh7110_obj->peCore->ofm_start_l = (dram_output_addr + (i + j) * jh7110_obj->peCore->ofm_width * jh7110_obj->peCore->ofm_height) & 0xffffffff;
                jh7110_obj->peCore->ofm_addr_w_offset = ((jh7110_obj->peCore->ofm_width) & 0xfff);
                jh7110_obj->peCore->padding_word = -127;
                jh7110_obj->peCore->padding_size = padding_size;
                jh7110_obj->peCore->pooling_stride = ((stride_h & 0xf) << 4) + (stride_w & 0xf);
                jh7110_obj->peCore->reduce_mode = 0x0;
                jh7110_obj->peCore->ifm_rd_step_config = ((ifm_width_aligned / 16) & 0xffff) + (((((ifm_width_aligned / 16) & 0xffff) * stride_h) & 0xffff) << 16);
                jh7110_obj->peCore->vector_start = true;
                jh7110_obj->peCore->Run();
            }
        }
    }
    //2. do multi line each run
    else if (ifm_width_aligned <= 1024){
        int line_num_each_run = 8192 / ifm_width_aligned;
        int line_num_cur_run;
        int line_num_reload;
        int line_idx = 0;
        int ofm_line_idx = 0;
        for (line_idx = 0; line_idx < ifm_num * ifm_height; line_idx += (line_num_cur_run - line_num_reload)) {
            line_num_cur_run = (((line_idx % ifm_height) + line_num_each_run) > ifm_height) ? (ifm_height - (line_idx % ifm_height)): line_num_each_run;
            line_num_reload = (((line_idx + line_num_cur_run) % ifm_height) == 0) ? 0 : line_num_cur_run - (((line_num_cur_run + pad_t * ((line_idx % ifm_height) == 0) - knl_h) / stride_h + 1) * stride_h - pad_t * ((line_idx % ifm_height) == 0));

            if ((line_num_cur_run + pad_t * ((line_idx % ifm_height) == 0) + pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0)) < knl_h) {
                continue;
            }

            //dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + line_idx * ifm_width) & 0xf;
            jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_addr + line_idx * ifm_width) & (((u64)0xffffffff) << 4)) >> 4);
            jh7110_obj->dma->dramAddrStep[0] = ifm_width;
            jh7110_obj->dma->burstDataLen[0] = ifm_width;
            jh7110_obj->dma->burstDataTimes[0] = line_num_cur_run;
            jh7110_obj->dma->writeDataLen[0] = ifm_width;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = ifm_width_aligned;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;
            jh7110_obj->dma->vgrEn[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            //pooling
            jh7110_obj->peCore->irq_status_reg = false;
            jh7110_obj->peCore->irq_mask_reg = false;
            jh7110_obj->peCore->vector_type = vector_type;
            jh7110_obj->peCore->ifm_start_addr = (((0) / 16) & 0xffff);
            jh7110_obj->peCore->ifm_width = ifm_width;
            jh7110_obj->peCore->ifm_height = line_num_cur_run;
            jh7110_obj->peCore->ifm_size = ifm_width * line_num_cur_run;
            jh7110_obj->peCore->ofm_width = (ifm_width + pad_l + pad_r - knl_w) / stride_w + 1;
            jh7110_obj->peCore->ofm_height = (line_num_cur_run +
                                              pad_t * ((line_idx % ifm_height) == 0) +
                                              pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0) -
                                              knl_h) / stride_h + 1;
            jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + ofm_line_idx * jh7110_obj->peCore->ofm_width) & (((u64)0xf) << 32)) >> 32);
            jh7110_obj->peCore->ofm_start_l = (dram_output_addr + ofm_line_idx * jh7110_obj->peCore->ofm_width) & 0xffffffff;
            jh7110_obj->peCore->ofm_addr_w_offset = ((jh7110_obj->peCore->ofm_width) & 0xfff);
            jh7110_obj->peCore->padding_word = -127;

            u32 padding_size = 0;
            if(pad_l){
                padding_size |= ((pad_l & 0xf) << 0);
            }
            if(pad_r){
                padding_size |= ((pad_r & 0xf) << 4);
            }
            if(pad_t){
                padding_size |= (((pad_t * ((line_idx % ifm_height) == 0)) & 0xf) << 8);
            }
            if(pad_b){
                padding_size |= (((pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0)) & 0xf) << 12);
            }

            jh7110_obj->peCore->padding_size = padding_size;
            jh7110_obj->peCore->pooling_stride = ((stride_h & 0xf) << 4) + (stride_w & 0xf);
            jh7110_obj->peCore->reduce_mode = 0x0;
            jh7110_obj->peCore->ifm_rd_step_config = ((ifm_width_aligned / 16) & 0xffff) + (((((ifm_width_aligned / 16) & 0xffff) * stride_h) & 0xffff) << 16);
            jh7110_obj->peCore->vector_start = true;
            jh7110_obj->peCore->Run();
            ofm_line_idx += jh7110_obj->peCore->ofm_height;
        }
    }
    //3. do tile division
    else {
        int col_num_each_run = 1024;
        int line_num_each_run = 8192 / 1024;
        int line_idx = 0;
        int line_num_cur_run;
        int line_num_reload;
        int col_idx = 0;
        int col_num_cur_run;
        int col_num_reload;
        int ofm_line_idx = 0;
        for (line_idx = 0; line_idx < ifm_num * ifm_height; line_idx += (line_num_cur_run - line_num_reload)) {
            line_num_cur_run = (((line_idx % ifm_height) + line_num_each_run) > ifm_height) ? (ifm_height - (line_idx % ifm_height)): line_num_each_run;
            line_num_reload = (((line_idx + line_num_cur_run) % ifm_height) == 0) ? 0 : line_num_cur_run - (((line_num_cur_run + pad_t * ((line_idx % ifm_height) == 0) - knl_h) / stride_h + 1) * stride_h - pad_t * ((line_idx % ifm_height) == 0));

            if ((line_num_cur_run + pad_t * ((line_idx % ifm_height) == 0) + pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0)) < knl_h) {
                continue;
            }

            int ofm_col_idx = 0;
            for (col_idx = 0; col_idx < ifm_width; col_idx += (col_num_cur_run - col_num_reload)) {
                col_num_cur_run = ((ifm_width - col_idx) > col_num_each_run) ? col_num_each_run : (ifm_width - col_idx);
                col_num_reload = ((col_idx + col_num_cur_run) == ifm_width) ? 0 : col_num_cur_run - (((col_num_cur_run + pad_l * (col_idx == 0) - knl_w) / stride_w + 1) * stride_w - pad_l * (col_idx == 0));

                if ((col_num_cur_run + pad_l * (col_idx == 0) + pad_r * ((col_idx + col_num_cur_run) == ifm_width)) < knl_w) {
                    continue;
                }

                //dma
                jh7110_obj->dma->vgrEn[0] = 1;
                jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + line_idx * ifm_width + col_idx) & 0xf;
                jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_addr + line_idx * ifm_width + col_idx) & (((u64)0xffffffff) << 4)) >> 4);
                jh7110_obj->dma->dramAddrStep[0] = ifm_width;
                jh7110_obj->dma->burstDataLen[0] = col_num_cur_run;
                jh7110_obj->dma->burstDataTimes[0] = line_num_cur_run;
                jh7110_obj->dma->writeDataLen[0] = col_num_cur_run;
                jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
                jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
                jh7110_obj->dma->ramWaddrStep[0] = (col_num_cur_run + 16 - 1) / 16 * 16;
                jh7110_obj->dma->mode[0] = 0x0;
                jh7110_obj->dma->scale[0] = 0;
                jh7110_obj->dma->padZero[0] = 0;
                jh7110_obj->dma->vgrEn[1] = 0;
                jh7110_obj->dma->convDmaEn = 1;
                jh7110_obj->dma->Run();

                //pooling
                jh7110_obj->peCore->irq_status_reg = false;
                jh7110_obj->peCore->irq_mask_reg = false;
                jh7110_obj->peCore->vector_type = vector_type;
                jh7110_obj->peCore->ifm_start_addr = (((0) / 16) & 0xffff);
                jh7110_obj->peCore->ifm_width = col_num_cur_run;
                jh7110_obj->peCore->ifm_height = line_num_cur_run;
                jh7110_obj->peCore->ifm_size = col_num_cur_run * line_num_cur_run;
                jh7110_obj->peCore->ofm_width = (col_num_cur_run +
                                                 pad_l * (col_idx == 0) +
                                                 pad_r * ((col_idx + col_num_cur_run) == ifm_width) -
                                                 knl_w) / stride_w + 1;
                jh7110_obj->peCore->ofm_height = (line_num_cur_run +
                                                  pad_t * ((line_idx % ifm_height) == 0) +
                                                  pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0) -
                                                  knl_h) / stride_h + 1;
                jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + ofm_line_idx * ofm_width + ofm_col_idx) & (((u64)0xf) << 32)) >> 32);
                jh7110_obj->peCore->ofm_start_l = (dram_output_addr + ofm_line_idx * ofm_width + ofm_col_idx) & 0xffffffff;

                jh7110_obj->peCore->ofm_addr_w_offset = (ofm_width & 0xfff);
                jh7110_obj->peCore->padding_word = -127;

                u32 padding_size = 0;
                if(pad_l){
                    padding_size |= (((pad_l * (col_idx == 0)) & 0xf) << 0);
                }
                if(pad_r){
                    padding_size |= (((pad_r * ((col_idx + col_num_cur_run) == ifm_width)) & 0xf) << 4);
                }
                if(pad_t){
                    padding_size |= (((pad_t * ((line_idx % ifm_height) == 0)) & 0xf) << 8);
                }
                if(pad_b){
                    padding_size |= (((pad_b * (((line_idx + line_num_cur_run) % ifm_height) == 0)) & 0xf) << 12);
                }

                jh7110_obj->peCore->padding_size = padding_size;
                jh7110_obj->peCore->pooling_stride = ((stride_h & 0xf) << 4) + (stride_w & 0xf);
                jh7110_obj->peCore->reduce_mode = 0x0;
                jh7110_obj->peCore->ifm_rd_step_config = (((col_num_cur_run + 16 - 1) / 16) & 0xffff) + ((((((col_num_cur_run + 16 - 1) / 16) & 0xffff) * stride_h) & 0xffff) << 16);
                jh7110_obj->peCore->vector_start = true;
                jh7110_obj->peCore->Run();
                ofm_col_idx += jh7110_obj->peCore->ofm_width;
            }
            ofm_line_idx += jh7110_obj->peCore->ofm_height;
        }
    }
    jh7110_obj->dram->read_mem(output, dram_output_addr, ofm_size);
    return;
}

void ewadd_op(void *input_1, void *input_2, void *output,
              int ifm_size, int scale_num_1, int scale_num_2,
              int scale_shift_1, int scale_shift_2)
{

    auto jh7110_obj = std::make_shared<JH7110>();

    u64 dram_input_1_addr = 0x0;
    jh7110_obj->dram->write_mem(dram_input_1_addr, input_1, ifm_size);
    //align to 16 byte
    u64 dram_input_2_addr = (dram_input_1_addr + ifm_size + 16 - 1) / 16 * 16;
    jh7110_obj->dram->write_mem(dram_input_2_addr, input_2, ifm_size);

    u64 dram_output_addr = (dram_input_2_addr + ifm_size + 16 - 1) / 16 * 16;

    u32 remain_byte;
    u32 burst_len;
    for (int i = 0; i < ifm_size; i += burst_len) {
        remain_byte = ifm_size - i;
        burst_len = (remain_byte < 4096) ? remain_byte : 4096;

        //ifm1
        jh7110_obj->dma->vgrEn[0] = 1;
        jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_1_addr + i) & 0xf;
        jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_1_addr + i) & (((u64)0xffffffff) << 4)) >> 4);
        jh7110_obj->dma->dramAddrStep[0] = burst_len;
        jh7110_obj->dma->burstDataLen[0] = burst_len;
        jh7110_obj->dma->burstDataTimes[0] = 1;
        jh7110_obj->dma->writeDataLen[0] = burst_len;
        jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
        jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
        jh7110_obj->dma->ramWaddrStep[0] = burst_len;
        jh7110_obj->dma->mode[0] = 0x0;
        jh7110_obj->dma->scale[0] = 0;
        jh7110_obj->dma->padZero[0] = 0;

        //ifm2
        jh7110_obj->dma->vgrEn[1] = 1;
        jh7110_obj->dma->vgrRaddr_l[1] = (dram_input_2_addr + i ) & 0xf;
        jh7110_obj->dma->vgrRaddr_h[1] = (u32)((((u64)dram_input_2_addr + i) & (((u64)0xffffffff) << 4)) >> 4);
        jh7110_obj->dma->dramAddrStep[1] = burst_len;
        jh7110_obj->dma->burstDataLen[1] = burst_len;
        jh7110_obj->dma->burstDataTimes[1] = 1;
        jh7110_obj->dma->writeDataLen[1] = burst_len;
        jh7110_obj->dma->ramWaddr[1] = ((jh7110_obj->peCoreStartAddr + 4096) & ((0xffffffff) << 5)) >> 5;
        jh7110_obj->dma->ramWaddr_offset[1] = ((jh7110_obj->peCoreStartAddr + 4096) & 0x1f);
        jh7110_obj->dma->ramWaddrStep[1] = burst_len;
        jh7110_obj->dma->mode[1] = 0x0;
        jh7110_obj->dma->scale[1] = 0;
        jh7110_obj->dma->padZero[1] = 0;
        jh7110_obj->dma->convDmaEn = 1;
        jh7110_obj->dma->Run();

        //bool vector_start;
        jh7110_obj->peCore->irq_status_reg = false;
        jh7110_obj->peCore->irq_mask_reg = false;
        jh7110_obj->peCore->vector_type = (0x1 << 0);
        jh7110_obj->peCore->ifm_start_addr = (((4096 / 16) & 0xffff) << 16) + (0 & 0xffff);
        jh7110_obj->peCore->ifm_size = burst_len;
        jh7110_obj->peCore->ofm_height = 1;
        jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + i) & (((u64)0xf) << 32)) >> 32);
        jh7110_obj->peCore->ofm_start_l = (dram_output_addr + i) & 0xffffffff;
        jh7110_obj->peCore->scale_shift = ((scale_shift_2 & 0x1f) << 5) + (scale_shift_1 & 0x1f);
        jh7110_obj->peCore->scale_num = ((scale_num_2 & 0xffff) << 16) + (scale_num_1 & 0xffff);
        jh7110_obj->peCore->vector_start = true;
        jh7110_obj->peCore->Run();
    }

    jh7110_obj->dram->read_mem(output, dram_output_addr, ifm_size);
    return;
}

void ewmul_op(void *input_1, void *input_2, void *output,
              int ifm_size, int scale_num, int scale_shift)
{

    auto jh7110_obj = std::make_shared<JH7110>();
    u64 dram_input_1_addr = 0x0;
    jh7110_obj->dram->write_mem(dram_input_1_addr, input_1, ifm_size);
    //align to 16 byte
    u64 dram_input_2_addr = (dram_input_1_addr + ifm_size + 16 - 1) / 16 * 16;
    jh7110_obj->dram->write_mem(dram_input_2_addr, input_2, ifm_size);

    u64 dram_output_addr = (dram_input_2_addr + ifm_size + 16 - 1) / 16 * 16;

    u32 remain_byte;
    u32 burst_len;
    for (int i = 0; i < ifm_size; i += burst_len) {
        remain_byte = ifm_size - i;
        burst_len = (remain_byte < 4096) ? remain_byte : 4096;

        //ifm1
        jh7110_obj->dma->vgrEn[0] = 1;
        jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_1_addr + i) & 0xf;
        jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_1_addr + i) & (((u64)0xffffffff) << 4)) >> 4);
        jh7110_obj->dma->dramAddrStep[0] = burst_len;
        jh7110_obj->dma->burstDataLen[0] = burst_len;
        jh7110_obj->dma->burstDataTimes[0] = 1;
        jh7110_obj->dma->writeDataLen[0] = burst_len;
        jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((0xffffffff) << 5)) >> 5;
        jh7110_obj->dma->ramWaddr_offset[0] =  ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
        jh7110_obj->dma->ramWaddrStep[0] = burst_len;
        jh7110_obj->dma->mode[0] = 0x0;
        jh7110_obj->dma->scale[0] = 0;
        jh7110_obj->dma->padZero[0] = 0;

        //ifm2
        jh7110_obj->dma->vgrEn[1] = 1;
        jh7110_obj->dma->vgrRaddr_l[1] = (dram_input_2_addr + i) & 0xf;
        jh7110_obj->dma->vgrRaddr_h[1] = (u32)((((u64)dram_input_2_addr + i ) & (((u64)0xffffffff) << 4)) >> 4);
        jh7110_obj->dma->dramAddrStep[1] = burst_len;
        jh7110_obj->dma->burstDataLen[1] = burst_len;
        jh7110_obj->dma->burstDataTimes[1] = 1;
        jh7110_obj->dma->writeDataLen[1] = burst_len;
        jh7110_obj->dma->ramWaddr[1] = ((4096 + jh7110_obj->peCoreStartAddr) & ((0xffffffff) << 5)) >> 5;
        jh7110_obj->dma->ramWaddr_offset[1] = ((4096 + jh7110_obj->peCoreStartAddr) & 0x1f);
        jh7110_obj->dma->ramWaddrStep[1] = burst_len;
        jh7110_obj->dma->mode[1] = 0x0;
        jh7110_obj->dma->scale[1] = 0;
        jh7110_obj->dma->padZero[1] = 0;
        jh7110_obj->dma->convDmaEn = 1;
        jh7110_obj->dma->Run();

        //bool vector_start;
        jh7110_obj->peCore->irq_status_reg = false;
        jh7110_obj->peCore->irq_mask_reg = false;
        jh7110_obj->peCore->vector_type = (0x1 << 1);
        jh7110_obj->peCore->ifm_start_addr = (((4096 / 16) & 0xffff) << 16) + (0 & 0xffff);
        jh7110_obj->peCore->ifm_size = burst_len;
        jh7110_obj->peCore->ofm_height = 1;
        jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + i) & (((u64)0xf) << 32)) >> 32);
        jh7110_obj->peCore->ofm_start_l = (dram_output_addr + i) & 0xffffffff;
        jh7110_obj->peCore->scale_shift = (scale_shift & 0x3f);
        jh7110_obj->peCore->scale_num = (scale_num & 0xffffffff);
        jh7110_obj->peCore->vector_start = true;
        jh7110_obj->peCore->Run();
    }

    jh7110_obj->dram->read_mem(output, dram_output_addr, ifm_size);
    return;
}

void gap_op(void *input, void *output, int ifm_width,
            int ifm_height, int ifm_num, int scale_num,
            int scale_shift)
{
    auto jh7110_obj = std::make_unique<JH7110>();

    int ifm_size = ifm_num * ifm_height * ifm_width;
    int ifm_size_per_ch = ifm_height * ifm_width;
    u64 dram_input_addr = jh7110_obj->dramStartAddr;
    jh7110_obj->dram->write_mem(dram_input_addr, input, ifm_size);

    u64 dram_output_addr = dram_input_addr + ifm_size;

    //vector pass sram line alignment
    constexpr int vector_sram_line_align = 16;
    if (ifm_size_per_ch > 8192) {
        //TODO: support bigger ifm
        //std::exit(-1);
        CHECK_MSG(false, "Can not support ifm_size > 8192");
    }
    else {
        int ifm_size_per_ch_aligned = (ifm_size_per_ch + vector_sram_line_align - 1) / vector_sram_line_align * vector_sram_line_align;
        int max_ifm_num_each_run = 8192 / ifm_size_per_ch_aligned;
        int remain_ifm_num;
        int ifm_num_cur_run;
        for (int i = 0; i < ifm_num; i += ifm_num_cur_run) {
            remain_ifm_num = ifm_num - i;
            ifm_num_cur_run = (remain_ifm_num > max_ifm_num_each_run ? max_ifm_num_each_run : remain_ifm_num);

            //ifm dma
            jh7110_obj->dma->vgrEn[0] = 1;
            jh7110_obj->dma->vgrRaddr_l[0] = (dram_input_addr + i * ifm_size_per_ch) & 0xf;
            jh7110_obj->dma->vgrRaddr_h[0] = (u32)((((u64)dram_input_addr + i * ifm_size_per_ch) & (((u64)0xffffffff) << 4)) >> 4);
            jh7110_obj->dma->dramAddrStep[0] = ifm_size_per_ch;
            jh7110_obj->dma->burstDataLen[0] = ifm_size_per_ch;
            jh7110_obj->dma->burstDataTimes[0] = ifm_num_cur_run;
            jh7110_obj->dma->writeDataLen[0] = ifm_size_per_ch;
            jh7110_obj->dma->ramWaddr[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & ((u64)(0xffffffff) << 5)) >> 5;
            jh7110_obj->dma->ramWaddr_offset[0] = ((jh7110_obj->peCoreStartAddr + 0x0) & 0x1f);
            jh7110_obj->dma->ramWaddrStep[0] = ifm_size_per_ch_aligned;
            jh7110_obj->dma->mode[0] = 0x0;
            jh7110_obj->dma->scale[0] = 0;
            jh7110_obj->dma->padZero[0] = 0;
            jh7110_obj->dma->vgrEn[1] = 0;
            jh7110_obj->dma->convDmaEn = 1;
            jh7110_obj->dma->Run();

            //bool vector_start;
            jh7110_obj->peCore->irq_status_reg = false;
            jh7110_obj->peCore->irq_mask_reg = false;
            jh7110_obj->peCore->vector_type = (0x1 << 8);
            jh7110_obj->peCore->ifm_start_addr = (0 & 0xffff);
            jh7110_obj->peCore->ifm_width = ifm_width * ifm_height;
            jh7110_obj->peCore->ifm_height = ifm_num_cur_run;
            jh7110_obj->peCore->ifm_size = ifm_size_per_ch;
            jh7110_obj->peCore->ofm_width = ifm_num_cur_run;
            jh7110_obj->peCore->ofm_height = 1;
            jh7110_obj->peCore->ofm_start_h = (u32)((((u64)dram_output_addr + i) & (((u64)0xf) << 32)) >> 32);
            jh7110_obj->peCore->ofm_start_l = (dram_output_addr + i) & 0xffffffff;
            jh7110_obj->peCore->scale_shift = (scale_shift & 0x3f);
            jh7110_obj->peCore->scale_num = (scale_num & 0xffffffff);
            jh7110_obj->peCore->reduce_mode = 0x1;
            jh7110_obj->peCore->vector_start = true;
            jh7110_obj->peCore->Run();
        }
        jh7110_obj->dram->read_mem(output, dram_output_addr, ifm_num);
    }
    return;
}

void upsampling_op(void *input, void *output, int ifm_height,
                   int ifm_width, int ifm_num, int h_factor,
                   int w_factor)
{
    auto jh7110_obj = std::make_unique<JH7110>();

    int ifm_size = ifm_num * ifm_height * ifm_width;
    int flatten_ifm_height = ifm_height * ifm_num;
    int ofm_h = ifm_height * h_factor;
    int ofm_w = ifm_width * w_factor;
    int ofm_size = ofm_h * ofm_w * ifm_num;

    u64 dram_input_addr = jh7110_obj->dramStartAddr;
    u64 dram_output_addr = dram_input_addr + ifm_size;

    if (h_factor > 4 || w_factor > 4) {
        CHECK_MSG(false, "The factor must be smaller than 4");
    }

    // Prepare inputs
    jh7110_obj->dram->write_mem(dram_input_addr, input, ifm_size);

    // DMA configuration
    jh7110_obj->dma->convDmaEn = 1;
    jh7110_obj->dma->mode[0] = 4;
    jh7110_obj->dma->vgrRaddr_l[0] = dram_input_addr & 0xf;
    jh7110_obj->dma->vgrRaddr_h[0] = (u32)(((u64)dram_input_addr & (((u64)0xffffffff) << 4)) >> 4);
    jh7110_obj->dma->dramAddrStep[0] = ifm_width;
    jh7110_obj->dma->burstDataLen[0] = ifm_width;
    jh7110_obj->dma->burstDataTimes[0] = flatten_ifm_height;
    jh7110_obj->dma->writeDataLen[0] = ofm_w;
    jh7110_obj->dma->ramWaddr[0] = (dram_output_addr & (((u64)0xffffffff) << 5)) >> 5;
    jh7110_obj->dma->ramWaddr_offset[0] = (dram_output_addr & 0x1f);
    jh7110_obj->dma->ramWaddrStep[0] = ofm_w;
    jh7110_obj->dma->scale[0] = ((h_factor - 1) << 3) + (w_factor - 1);
    jh7110_obj->dma->vgrEn[0] = 1;

    jh7110_obj->dma->Run();

    // Fetch outputs
    jh7110_obj->dram->read_mem(output, dram_output_addr, ofm_size);
}
